diff --git a/.editorconfig b/.editorconfig
index 01a20f16fe3..9d08a1a828a 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,5 +1,3 @@
-# https://editorconfig.org
-
 root = true
 
 [*]
@@ -9,7 +7,3 @@ indent_size = 2
 end_of_line = lf
 insert_final_newline = true
 trim_trailing_whitespace = true
-
-[*.md]
-insert_final_newline = false
-trim_trailing_whitespace = false
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 00000000000..98bc4890f37
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,7 @@
+{
+  "extends": "vue",
+  "rules": {
+    "no-duplicate-imports": 0,
+    "no-useless-escape": 0
+  }
+}
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
deleted file mode 100644
index 53dbf616c0a..00000000000
--- a/.git-blame-ignore-revs
+++ /dev/null
@@ -1,4 +0,0 @@
-# chore: move to typescript
-af9fc2bcff31d5baa413039818a9b3e011deccaf
-# workflow: remove eslint, apply prettier
-72aed6a149b94b5b929fb47370a7a6d4cb7491c5
diff --git a/.github/COMMIT_CONVENTION.md b/.github/COMMIT_CONVENTION.md
deleted file mode 100644
index 381bf17baab..00000000000
--- a/.github/COMMIT_CONVENTION.md
+++ /dev/null
@@ -1,91 +0,0 @@
-## Git Commit Message Convention
-
-> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).
-
-#### TL;DR:
-
-Messages must be matched by the following regex:
-
-``` js
-/^(revert: )?(feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types)(\(.+\))?: .{1,50}/
-```
-
-#### Examples
-
-Appears under "Features" header, `compiler` subheader:
-
-```
-feat(compiler): add 'comments' option
-```
-
-Appears under "Bug Fixes" header, `v-model` subheader, with a link to issue #28:
-
-```
-fix(v-model): handle events on blur
-
-close #28
-```
-
-Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
-
-```
-perf(core): improve vdom diffing by removing 'foo' option
-
-BREAKING CHANGE: The 'foo' option has been removed.
-```
-
-The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
-
-```
-revert: feat(compiler): add 'comments' option
-
-This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
-```
-
-### Full Message Format
-
-A commit message consists of a **header**, **body** and **footer**.  The header has a **type**, **scope** and **subject**:
-
-```
-<type>(<scope>): <subject>
-<BLANK LINE>
-<body>
-<BLANK LINE>
-<footer>
-```
-
-The **header** is mandatory and the **scope** of the header is optional.
-
-### Revert
-
-If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
-
-### Type
-
-If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However, if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.
-
-Other prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks.
-
-### Scope
-
-The scope could be anything specifying the place of the commit change. For example `core`, `compiler`, `ssr`, `v-model`, `transition` etc...
-
-### Subject
-
-The subject contains a succinct description of the change:
-
-* use the imperative, present tense: "change" not "changed" nor "changes"
-* don't capitalize the first letter
-* no dot (.) at the end
-
-### Body
-
-Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
-The body should include the motivation for the change and contrast this with previous behavior.
-
-### Footer
-
-The footer should contain any information about **Breaking Changes** and is also the place to
-reference GitHub issues that this commit **Closes**.
-
-**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
deleted file mode 100644
index 5a00cc5a319..00000000000
--- a/.github/CONTRIBUTING.md
+++ /dev/null
@@ -1,140 +0,0 @@
-# Vue.js Contributing Guide
-
-Hi! I'm really excited that you are interested in contributing to Vue.js. Before submitting your contribution, please make sure to take a moment and read through the following guidelines:
-
-- [Code of Conduct](https://github.com/vuejs/vue/blob/dev/.github/CODE_OF_CONDUCT.md)
-- [Issue Reporting Guidelines](#issue-reporting-guidelines)
-- [Pull Request Guidelines](#pull-request-guidelines)
-- [Development Setup](#development-setup)
-- [Project Structure](#project-structure)
-
-## Issue Reporting Guidelines
-
-- Always use [https://new-issue.vuejs.org/](https://new-issue.vuejs.org/) to create new issues.
-
-## Pull Request Guidelines
-
-- The `master` branch is just a snapshot of the latest stable release. All development should be done in dedicated branches. **Do not submit PRs against the `master` branch.**
-
-- Checkout a topic branch from the relevant branch, e.g. `dev`, and merge back against that branch.
-
-- Work in the `src` folder and **DO NOT** checkin `dist` in the commits.
-
-- It's OK to have multiple small commits as you work on the PR - GitHub will automatically squash it before merging.
-
-- Make sure `npm test` passes. (see [development setup](#development-setup))
-
-- If adding a new feature:
-
-  - Add accompanying test case.
-  - Provide a convincing reason to add this feature. Ideally, you should open a suggestion issue first and have it approved before working on it.
-
-- If fixing bug:
-  - If you are resolving a special issue, add `(fix #xxxx[,#xxxx])` (#xxxx is the issue id) in your PR title for a better release log, e.g. `update entities encoding/decoding (fix #3899)`.
-  - Provide a detailed description of the bug in the PR. Live demo preferred.
-  - Add appropriate test coverage if applicable.
-
-## Development Setup
-
-You will need [Node.js](https://nodejs.org) **version 18+** and [pnpm](https://pnpm.io/) **version 8+**.
-
-After cloning the repo, run:
-
-```bash
-$ pnpm i # install the dependencies of the project
-```
-
-### Committing Changes
-
-Commit messages should follow the [commit message convention](./COMMIT_CONVENTION.md) so that changelogs can be automatically generated. Commit messages will be automatically validated upon commit. If you are not familiar with the commit message convention, you can use `npm run commit` instead of `git commit`, which provides an interactive CLI for generating proper commit messages.
-
-### Commonly used NPM scripts
-
-```bash
-# watch and auto re-build dist/vue.js
-$ npm run dev
-
-# run unit tests
-$ npm run test:unit
-
-# run specific tests in watch mode
-$ npx vitest {test_file_name_pattern_to_match}
-
-# build all dist files, including npm packages
-$ npm run build
-
-# run the full test suite, including unit/e2e/type checking
-$ npm test
-```
-
-There are some other scripts available in the `scripts` section of the `package.json` file.
-
-The default test script will do the following: lint with ESLint -> type check with Flow -> unit tests with coverage -> e2e tests. **Please make sure to have this pass successfully before submitting a PR.** Although the same tests will be run against your PR on the CI server, it is better to have it working locally.
-
-## Project Structure
-
-- **`scripts`**: contains build-related scripts and configuration files. Usually, you don't need to touch them. However, it would be helpful to familiarize yourself with the following files:
-
-  - `scripts/alias.js`: module import aliases used across all source code and tests.
-
-  - `scripts/config.js`: contains the build configurations for all files found in `dist/`. Check this file if you want to find out the entry source file for a dist file.
-
-- **`dist`**: contains built files for distribution. Note this directory is only updated when a release happens; they do not reflect the latest changes in development branches.
-
-  See [dist/README.md](https://github.com/vuejs/vue/blob/dev/dist/README.md) for more details on dist files.
-
-- **`types`**: contains public types published to npm (note the types shipped here could be different from `src/types`). These were hand-authored before we moved the codebase from Flow to TypeScript. To ensure backwards compatibility, we keep using these manually authored types.
-
-  Types for new features added in 2.7 (Composition API) are auto-generated from source code as `types/v3-generated.d.ts` and re-exported from `types/index.d.ts`.
-
-- **`packages`**:
-
-  - `vue-server-renderer` and `vue-template-compiler` are distributed as separate NPM packages. They are automatically generated from the source code and always have the same version with the main `vue` package.
-
-  - `compiler-sfc` is an internal package that is distributed as part of the main `vue` package. It's aliased and can be imported as `vue/compiler-sfc` similar to Vue 3.
-
-- **`test`**: contains all tests. The unit tests are written with [Jasmine](http://jasmine.github.io/2.3/introduction.html) and run with [Karma](http://karma-runner.github.io/0.13/index.html). The e2e tests are written for and run with [Nightwatch.js](http://nightwatchjs.org/).
-
-- **`src`**: contains the source code. The codebase is written in ES2015 with [Flow](https://flowtype.org/) type annotations.
-
-  - **`compiler`**: contains code for the template-to-render-function compiler.
-
-    The compiler consists of a parser (converts template strings to element ASTs), an optimizer (detects static trees for vdom render optimization), and a code generator (generate render function code from element ASTs). Note that codegen directly generates code strings from the element AST - it's done this way for smaller code size because the compiler is shipped to the browser in the standalone build.
-
-  - **`core`**: contains universal, platform-agnostic runtime code.
-
-    The Vue 2.0 core is platform-agnostic. That is, the code inside `core` is able to be run in any JavaScript environment, be it the browser, Node.js, or an embedded JavaScript runtime in native applications.
-
-    - **`observer`**: contains code related to the reactivity system.
-
-    - **`vdom`**: contains code related to vdom element creation and patching.
-
-    - **`instance`**: contains Vue instance constructor and prototype methods.
-
-    - **`global-api`**: contains Vue global api.
-
-    - **`components`**: contains universal abstract components.
-
-  - **`server`**: contains code related to server-side rendering.
-
-  - **`platforms`**: contains platform-specific code.
-
-    Entry files for dist builds are located in their respective platform directory.
-
-    Each platform module contains three parts: `compiler`, `runtime` and `server`, corresponding to the three directories above. Each part contains platform-specific modules/utilities which are imported and injected to the core counterparts in platform-specific entry files. For example, the code implementing the logic behind `v-bind:class` is in `platforms/web/runtime/modules/class.js` - which is imported in `platforms/web/entry-runtime.ts` and used to create the browser-specific vdom patching function.
-
-  - **`sfc`**: contains single-file component (`*.vue` files) parsing logic. This is used in the `vue-template-compiler` package.
-
-  - **`shared`**: contains utilities shared across the entire codebase.
-
-  - **`types`**: contains type declarations added when we ported the codebase from Flow to TypeScript. These types should be considered internal - they care less about type inference for end-user scenarios and prioritize working with internal source code.
-
-## Financial Contribution
-
-As a pure community-driven project without major corporate backing, we also welcome financial contributions via GitHub Sponsors and OpenCollective. Please consult the [Sponsor Page](https://vuejs.org/sponsor/) for more details.
-
-## Credits
-
-Thank you to all the people who have already contributed to Vue.js!
-
-<a href="https://github.com/vuejs/vue/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890" /></a>
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index 652c1192b7b..00000000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-# These are supported funding model platforms
-
-github: [yyx990803, posva]
-patreon: evanyou
-open_collective: vuejs
-ko_fi: # Replace with a single Ko-fi username
-tidelift: npm/vue
-custom: # Replace with a single custom sponsorship URL
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
deleted file mode 100644
index 2d3d0ba085e..00000000000
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-blank_issues_enabled: false
-contact_links:
-  - name: Vue 2 has reached EOL!
-    url: https://v2.vuejs.org/eol/
-    about: Vue 2 has reached EOL and is no longer actively maintained. Click the link on the right for more details.
-  - name: Vue 2 NES by HeroDevs
-    url: https://www.herodevs.com/support/nes-vue?utm_source=vuejs-github&utm_medium=issue-form
-    about: Learn more about Vue 2 NES if you have security or compliance requirements for continued Vue 2 usage.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
deleted file mode 100644
index 21f32de041a..00000000000
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ /dev/null
@@ -1,35 +0,0 @@
-<!--
-Please make sure to read the Pull Request Guidelines:
-https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
--->
-
-<!-- PULL REQUEST TEMPLATE -->
-<!-- (Update "[ ]" to "[x]" to check a box) -->
-
-**What kind of change does this PR introduce?** (check at least one)
-
-- [ ] Bugfix
-- [ ] Feature
-- [ ] Code style update
-- [ ] Refactor
-- [ ] Build-related changes
-- [ ] Other, please describe:
-
-**Does this PR introduce a breaking change?** (check one)
-
-- [ ] Yes
-- [ ] No
-
-If yes, please describe the impact and migration path for existing applications:
-
-**The PR fulfills these requirements:**
-
-- [ ] It's submitted to the `main` branch for v2.x (or to a previous version branch)
-- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
-- [ ] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
-- [ ] New/updated tests are included
-
-If adding a **new feature**, the PR's description includes:
-- [ ] 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)
-
-**Other information:**
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 9a40dbcb701..00000000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,93 +0,0 @@
-name: 'ci'
-on:
-  push:
-    branches:
-      - main
-  pull_request:
-    branches:
-      - main
-jobs:
-  unit-test:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: Install pnpm
-        uses: pnpm/action-setup@v2
-
-      - name: Set node version to 18
-        uses: actions/setup-node@v2
-        with:
-          node-version: 18
-          cache: 'pnpm'
-
-      - run: pnpm install
-
-      - name: Run unit tests
-        run: pnpm run test:unit
-
-  ssr-sfc-test:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: Install pnpm
-        uses: pnpm/action-setup@v2
-
-      - name: Set node version to 18
-        uses: actions/setup-node@v2
-        with:
-          node-version: 18
-          cache: 'pnpm'
-
-      - run: pnpm install
-
-      - name: Run SSR tests
-        run: pnpm run test:ssr
-
-      - name: Run compiler-sfc tests
-        run: pnpm run test:sfc
-
-  e2e-test:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: Install pnpm
-        uses: pnpm/action-setup@v2
-
-      - name: Set node version to 18
-        uses: actions/setup-node@v2
-        with:
-          node-version: 18
-          cache: 'pnpm'
-
-      - run: pnpm install
-
-      - name: Run e2e tests
-        run: pnpm run test:e2e
-
-      - name: Run transition tests
-        run: pnpm run test:transition
-
-  type-test:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: Install pnpm
-        uses: pnpm/action-setup@v2
-
-      - name: Set node version to 18
-        uses: actions/setup-node@v2
-        with:
-          node-version: 18
-          cache: 'pnpm'
-
-      - run: pnpm install
-
-      - name: Run srouce type check
-        run: pnpm run ts-check
-
-      - name: Run type declaration tests
-        run: pnpm run test:types
diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml
deleted file mode 100644
index b698513080d..00000000000
--- a/.github/workflows/release-tag.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-on:
-  push:
-    tags:
-      - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
-
-name: Create Release
-
-jobs:
-  build:
-    name: Create Release
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout code
-        uses: actions/checkout@master
-      - name: Create Release for Tag
-        id: release_tag
-        uses: yyx990803/release-tag@master
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        with:
-          tag_name: ${{ github.ref }}
-          body: |
-            Please refer to [CHANGELOG.md](https://github.com/vuejs/vue/blob/main/CHANGELOG.md) for details.
diff --git a/.gitignore b/.gitignore
index 18c343efc53..a3faace8cf2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,17 +1,11 @@
-.DS_Store
-node_modules
-*.log
+dist/vue.js.map
+dist/vue.min.js.gz
+test/unit/specs.js
+test/unit/specs.js.map
 explorations
-TODOs.md
-RELEASE_NOTE*.md
-packages/server-renderer/basic.js
-packages/server-renderer/build.dev.js
-packages/server-renderer/build.prod.js
-packages/server-renderer/server-plugin.js
-packages/server-renderer/client-plugin.js
-packages/template-compiler/build.js
-packages/template-compiler/browser.js
-.vscode
-dist
-temp
-types/v3-generated.d.ts
+node_modules
+.DS_Store
+.idea
+benchmarks/browser.js
+coverage
+perf
diff --git a/.prettierrc b/.prettierrc
deleted file mode 100644
index ef93d94821a..00000000000
--- a/.prettierrc
+++ /dev/null
@@ -1,5 +0,0 @@
-semi: false
-singleQuote: true
-printWidth: 80
-trailingComma: 'none'
-arrowParens: 'avoid'
diff --git a/BACKERS.md b/BACKERS.md
deleted file mode 100644
index fa66d206698..00000000000
--- a/BACKERS.md
+++ /dev/null
@@ -1,9 +0,0 @@
-<h1 align="center">Sponsors &amp; Backers</h1>
-
-Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of the awesome sponsors and backers listed in this file. If you'd like to join them, please consider [ sponsor Vue's development](https://vuejs.org/sponsor/).
-
-<p align="center">
-  <a target="_blank" href="https://sponsors.vuejs.org/backers.svg">
-    <img alt="sponsors" src="https://sponsors.vuejs.org/backers.svg">
-  </a>
-</p>
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index d040c2c85b8..00000000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,1618 +0,0 @@
-## [2.7.16 Swan Song](https://github.com/vuejs/vue/compare/v2.7.16-beta.2...v2.7.16) (2023-12-24)
-
-
-### Bug Fixes
-
-* **lifecycle:** ensure component effect scopes are disconnected ([56ce7f8](https://github.com/vuejs/vue/commit/56ce7f8c573116ed6683149206cf35c987249d42)), closes [#13134](https://github.com/vuejs/vue/issues/13134)
-
-
-
-## [2.7.16-beta.2](https://github.com/vuejs/vue/compare/v2.7.16-beta.1...v2.7.16-beta.2) (2023-12-14)
-
-
-### Bug Fixes
-
-* account for nested render calls ([db9c566](https://github.com/vuejs/vue/commit/db9c566032da0ec5cd758a3e8525e9733874c1e5)), closes [#13131](https://github.com/vuejs/vue/issues/13131)
-* **types:** export more types for v3 alignment (jsx / component options) ([895669f](https://github.com/vuejs/vue/commit/895669ffa01f8075a326308caa8ad6a5f69a1919)), closes [#13078](https://github.com/vuejs/vue/issues/13078) [#13128](https://github.com/vuejs/vue/issues/13128)
-
-
-
-## [2.7.16-beta.1](https://github.com/vuejs/vue/compare/v2.7.15...v2.7.16-beta.1) (2023-12-08)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** check template `ref` usage,  ([#12985](https://github.com/vuejs/vue/issues/12985)) ([83d9535](https://github.com/vuejs/vue/commit/83d95351a9f809311d624fc3398e7f6829b72447)), closes [#12984](https://github.com/vuejs/vue/issues/12984)
-* **compiler-sfc:** fix rewriteDefault edge cases ([25f97a5](https://github.com/vuejs/vue/commit/25f97a5033187372e7b8c591c79336197ee5c833)), closes [#13060](https://github.com/vuejs/vue/issues/13060) [#12892](https://github.com/vuejs/vue/issues/12892) [#12906](https://github.com/vuejs/vue/issues/12906)
-* **keep-alive:** fix keep-alive memory leak ([2632249](https://github.com/vuejs/vue/commit/2632249925e632e56f6dfc8fdbcf682c82e4081b)), closes [#12827](https://github.com/vuejs/vue/issues/12827)
-* **keep-alive:** fix memory leak without breaking transition tests ([e0747f4](https://github.com/vuejs/vue/commit/e0747f40a879b4000a1959d21377b51d1f1ed988))
-* **props:** should not unwrap props that are raw refs ([08382f0](https://github.com/vuejs/vue/commit/08382f008016c3b3b93f84594266f2e191fee91d)), closes [#12930](https://github.com/vuejs/vue/issues/12930)
-* **shallowReactive:** should track value if already reactive when set in shallowReactive ([0ad8e8d](https://github.com/vuejs/vue/commit/0ad8e8d94f3a3bf4429f25850c85a6bbb2b81364))
-* **style:** always set new styles ([f5ef882](https://github.com/vuejs/vue/commit/f5ef882a781b8a62c9ca00e95006d07636567c8e)), closes [#12901](https://github.com/vuejs/vue/issues/12901) [#12946](https://github.com/vuejs/vue/issues/12946)
-* **types:** fix shallowRef's return type  ([#12979](https://github.com/vuejs/vue/issues/12979)) ([a174c29](https://github.com/vuejs/vue/commit/a174c29dab2cf655b06f7870e0ac5a78ef35ec8a)), closes [#12978](https://github.com/vuejs/vue/issues/12978)
-* **types:** fix type augmentation and compiler-sfc types w/moduleResolution: bundler ([#13107](https://github.com/vuejs/vue/issues/13107)) ([de0b97b](https://github.com/vuejs/vue/commit/de0b97b3eadae120eda505b45df2de2115dcb6f0)), closes [#13106](https://github.com/vuejs/vue/issues/13106)
-* **types:** provide types for built-in components ([3650c12](https://github.com/vuejs/vue/commit/3650c12f7d3a20f3155bc1fd2b068e84289e0d33)), closes [#13002](https://github.com/vuejs/vue/issues/13002)
-* **types:** type VNodeChildren should allow type number  ([#13067](https://github.com/vuejs/vue/issues/13067)) ([24fcf69](https://github.com/vuejs/vue/commit/24fcf69624a633d43dfc0aa5fa6b93d11de7fad5)), closes [#12973](https://github.com/vuejs/vue/issues/12973)
-* **utils:** unwrap refs when stringifying values in template ([ae3e4b1](https://github.com/vuejs/vue/commit/ae3e4b1c706b8d61a4a312ca5d23441df021b4b4)), closes [#12884](https://github.com/vuejs/vue/issues/12884) [#12888](https://github.com/vuejs/vue/issues/12888)
-* **watch:** new property addition should trigger deep watcher with getter ([6d857f5](https://github.com/vuejs/vue/commit/6d857f5bee275dc98106e3b2cbc7722f5ec0cfc0)), closes [#12967](https://github.com/vuejs/vue/issues/12967) [#12972](https://github.com/vuejs/vue/issues/12972)
-
-
-
-## [2.7.15](https://github.com/vuejs/vue/compare/v2.7.14...v2.7.15) (2023-10-23)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** add semicolon after `defineProps` statement ([#12879](https://github.com/vuejs/vue/issues/12879)) ([51fef2c](https://github.com/vuejs/vue/commit/51fef2ca69459c1175e105991f60511f1996e0c8))
-* **compiler-sfc:** fix macro usage in multi-variable declaration ([#12873](https://github.com/vuejs/vue/issues/12873)) ([d27c128](https://github.com/vuejs/vue/commit/d27c128b7cb1640f3aa185a5ecdea4ff35763794))
-* **compiler-sfc:** Optimize the value of emitIdentifier ([#12851](https://github.com/vuejs/vue/issues/12851)) ([bb59751](https://github.com/vuejs/vue/commit/bb59751dd4e45afcaafd607f22505a724b1ef841))
-* **compiler-sfc:** Resolve object expression parsing errors in `v-on` ([#12862](https://github.com/vuejs/vue/issues/12862)) ([b8c8b3f](https://github.com/vuejs/vue/commit/b8c8b3fc7a211744fdabd237a1a986a1f80b7c43))
-* **lifecycle:** scope might changed when call hook ([#13070](https://github.com/vuejs/vue/issues/13070)) ([74ca5a1](https://github.com/vuejs/vue/commit/74ca5a13ba12a31580f1567e7c6d789e96730e46))
-* **patch:** clone insert hooks to avoid being mutated during iteration ([#12905](https://github.com/vuejs/vue/issues/12905)) ([c223634](https://github.com/vuejs/vue/commit/c22363425ae246ccbb8418342e94edfa270d93e5))
-* **types/sfc:** improve the type inference using `withDefaults` ([#12872](https://github.com/vuejs/vue/issues/12872)) ([099401e](https://github.com/vuejs/vue/commit/099401e227fd5ed496ff615528d1a9b3b64d4fbf))
-* **types:** correct serverPrefetch this type ([#13068](https://github.com/vuejs/vue/issues/13068)) ([67c1d26](https://github.com/vuejs/vue/commit/67c1d26cb0af3eb2db0a11fc7768a8299e7f7d58)), closes [#12488](https://github.com/vuejs/vue/issues/12488)
-
-
-
-## [2.7.14](https://github.com/vuejs/vue/compare/v2.7.13...v2.7.14) (2022-11-09)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** fix template usage check edge case for v-slot destructured default value ([#12842](https://github.com/vuejs/vue/issues/12842)) ([5e3d4e9](https://github.com/vuejs/vue/commit/5e3d4e90cdf92ec0a72bbb2bd44125f1faafae1d)), closes [#12841](https://github.com/vuejs/vue/issues/12841)
-* **provide/inject:** do not mutate original provide options during merge ([d1899ca](https://github.com/vuejs/vue/commit/d1899caf688de961e63e7a0d56f806fc4a12efd9)), closes [#12854](https://github.com/vuejs/vue/issues/12854)
-* **reactivity:** avoid using WeakMap for IE compatibility ([29b5f58](https://github.com/vuejs/vue/commit/29b5f588032600baae9854ac9a4105916a5aa648)), closes [#12837](https://github.com/vuejs/vue/issues/12837)
-* **types:** fix spreading VNodeData in tsx ([#12789](https://github.com/vuejs/vue/issues/12789)) ([f7db7f3](https://github.com/vuejs/vue/commit/f7db7f361b6356591781b9f33abbb0d5b7f9b97c)), closes [#12778](https://github.com/vuejs/vue/issues/12778)
-* **types:** stricter type condition for `EventHandlers` ([#12840](https://github.com/vuejs/vue/issues/12840)) ([0b3cf7d](https://github.com/vuejs/vue/commit/0b3cf7dda9ac605b2b9f799acacd2793e974f225)), closes [#12832](https://github.com/vuejs/vue/issues/12832)
-
-
-
-## [2.7.13](https://github.com/vuejs/vue/compare/v2.7.12...v2.7.13) (2022-10-14)
-
-
-### Bug Fixes
-
-* **effectScope:** calling off() of a detached scope should not break currentScope ([800207c](https://github.com/vuejs/vue/commit/800207c473c7d6dfcdc883100a3d443fc5ad2e39)), closes [#12825](https://github.com/vuejs/vue/issues/12825)
-* **types:** style attribute svg ([#12800](https://github.com/vuejs/vue/issues/12800)) ([8e26261](https://github.com/vuejs/vue/commit/8e262618cdc3251ca9630b17de4a000567ffb007))
-* **watch:** avoid traversing objects that are marked non-reactive ([#12806](https://github.com/vuejs/vue/issues/12806)) ([5960f05](https://github.com/vuejs/vue/commit/5960f05c69099c174062b6672c7a21d717a3bccf))
-
-
-
-## [2.7.12](https://github.com/vuejs/vue/compare/v2.7.11...v2.7.12) (2022-10-12)
-
-
-### Reverts
-
-* Revert "fix(setup): setup hook should be called before beforeCreate" ([e80cd09](https://github.com/vuejs/vue/commit/e80cd09fff570df57d608f8f5aaccee6d7f31917)), closes [#12802](https://github.com/vuejs/vue/issues/12802) [#12821](https://github.com/vuejs/vue/issues/12821) [#12822](https://github.com/vuejs/vue/issues/12822)
-
-
-
-## [2.7.11](https://github.com/vuejs/vue/compare/v2.7.10...v2.7.11) (2022-10-11)
-
-
-### Bug Fixes
-
-* **build:** enforce LF line ending in built files ([738f4b3](https://github.com/vuejs/vue/commit/738f4b3c570dc3a1818924a203a9f8e4b1ec90f0)), closes [#12819](https://github.com/vuejs/vue/issues/12819)
-* **compiler-sfc:** export parseComponent for compat with fork-ts-checker-webpack-plugin ([0d6d972](https://github.com/vuejs/vue/commit/0d6d972b32521fd18eb853b1073c0a19859a499a)), closes [#12719](https://github.com/vuejs/vue/issues/12719)
-* **reactivity:** check skip first before checking ref when creating observer ([#12813](https://github.com/vuejs/vue/issues/12813)) ([5d26f81](https://github.com/vuejs/vue/commit/5d26f815c643d41e6ca6f29329593223b981fc24)), closes [#12812](https://github.com/vuejs/vue/issues/12812)
-* **reactivity:** use WeakMap for proxy/raw checks, compat with non-extensible objects ([4a0d88e](https://github.com/vuejs/vue/commit/4a0d88e46e4180edc7f22e36c25df3f8ac5d60d2)), closes [#12799](https://github.com/vuejs/vue/issues/12799) [#12798](https://github.com/vuejs/vue/issues/12798)
-* **setup:** setup hook should be called before beforeCreate ([e1342df](https://github.com/vuejs/vue/commit/e1342df7847a51c75192fec74e94378178e046b0)), closes [#12802](https://github.com/vuejs/vue/issues/12802)
-* **sfc:** prune returned bindings for non-TS as well ([fb13930](https://github.com/vuejs/vue/commit/fb1393009660b38046b1f6dfb532b481cc53b3b7)), closes [#12765](https://github.com/vuejs/vue/issues/12765)
-* **sfc:** remove sfc scoped deep syntax deprecation warnings ([2f335b2](https://github.com/vuejs/vue/commit/2f335b2f9d09b962f40e38740826d444e4fff073))
-* **types:** fix error with options watch ([#12779](https://github.com/vuejs/vue/issues/12779)) ([bc5b92a](https://github.com/vuejs/vue/commit/bc5b92adde147436f2adb25e457f0c967829467f)), closes [#12780](https://github.com/vuejs/vue/issues/12780)
-* **types:** support Ref and function types in tsx ref attribute ([#12759](https://github.com/vuejs/vue/issues/12759)) ([87f69aa](https://github.com/vuejs/vue/commit/87f69aa26f195390b948fbb0ff62cf954b58c82c)), closes [#12758](https://github.com/vuejs/vue/issues/12758)
-* **types:** vue 3 directive type compatibility ([#12792](https://github.com/vuejs/vue/issues/12792)) ([27eed82](https://github.com/vuejs/vue/commit/27eed829ccf9978a63b8cd989ff4c03897276bc2))
-
-
-### Performance Improvements
-
-* improve unsub perf for deps with massive amount of subs ([8880b55](https://github.com/vuejs/vue/commit/8880b55d52f8d873f79ef67436217c8752cddef5)), closes [#12696](https://github.com/vuejs/vue/issues/12696)
-
-
-
-## [2.7.10](https://github.com/vuejs/vue/compare/v2.7.9...v2.7.10) (2022-08-23)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** avoid deindent when lang is jsx/tsx ([46ca7bc](https://github.com/vuejs/vue/commit/46ca7bcddc06c50796ccff82d8c45693f1f14f47)), closes [#12755](https://github.com/vuejs/vue/issues/12755)
-* fix parent of multi-nested HOC $el not updating ([#12757](https://github.com/vuejs/vue/issues/12757)) ([e0b26c4](https://github.com/vuejs/vue/commit/e0b26c483a1ba407a818b1fcba1a439df24e84a8)), closes [#12589](https://github.com/vuejs/vue/issues/12589)
-* **types:** Add missing type parameter constraints ([#12754](https://github.com/vuejs/vue/issues/12754)) ([810f6d1](https://github.com/vuejs/vue/commit/810f6d12edea47cde7f39eaf7ec3ae1b7300d40c))
-
-
-
-## [2.7.9](https://github.com/vuejs/vue/compare/v2.7.8...v2.7.9) (2022-08-19)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** allow full hostnames in asset url base ([#12732](https://github.com/vuejs/vue/issues/12732)) ([5c742eb](https://github.com/vuejs/vue/commit/5c742eb2e0d8dad268fb29ed4f92d286b5e0f4b5)), closes [#12731](https://github.com/vuejs/vue/issues/12731)
-* **compiler-sfc:** rewriteDefault for class with decorators ([#12747](https://github.com/vuejs/vue/issues/12747)) ([5221d4d](https://github.com/vuejs/vue/commit/5221d4d3b6049c87d196d99dbb64bcd3f3b07279))
-* directives shorthand normalize error ([#12744](https://github.com/vuejs/vue/issues/12744)) ([2263948](https://github.com/vuejs/vue/commit/2263948c249e7486403bc5880712e6d9fd15c17f)), closes [#12743](https://github.com/vuejs/vue/issues/12743)
-* ensure render watcher of manually created instance is correctly tracked in owner scope ([bd89ce5](https://github.com/vuejs/vue/commit/bd89ce53a9de417a9372630bb5d433a40acc1a53)), closes [#12701](https://github.com/vuejs/vue/issues/12701)
-* fix effect scope tracking for manually created instances ([7161176](https://github.com/vuejs/vue/commit/7161176cd0dff10d65ab58e266018aff2660610f)), closes [#12705](https://github.com/vuejs/vue/issues/12705)
-* **ssr:** fix on-component directives rendering ([#12661](https://github.com/vuejs/vue/issues/12661)) ([165a14a](https://github.com/vuejs/vue/commit/165a14a6c6c406176037465d2961259c5c980399)), closes [#10733](https://github.com/vuejs/vue/issues/10733)
-* **types:** allow attaching unknown options to defined component ([b4bf4c5](https://github.com/vuejs/vue/commit/b4bf4c52ad31e02307cfd4d643dc5610c893e3ba)), closes [#12742](https://github.com/vuejs/vue/issues/12742)
-* **types:** fix missing error for accessing undefined instance properties ([8521f9d](https://github.com/vuejs/vue/commit/8521f9d3f63d26bde99b747f0cb14d0ac5ba5971)), closes [#12718](https://github.com/vuejs/vue/issues/12718)
-* **types:** fix options suggestions when using defineComponent ([4b37b56](https://github.com/vuejs/vue/commit/4b37b568c7c3fd238aa61fcc956f882223f8e87f)), closes [#12736](https://github.com/vuejs/vue/issues/12736)
-* **types:** Make SetupBindings optional on ExtendedVue and CombinedVueInstance ([#12727](https://github.com/vuejs/vue/issues/12727)) ([00458cd](https://github.com/vuejs/vue/commit/00458cd38d209410d3c675729230a42a0a34a4b9)), closes [#12726](https://github.com/vuejs/vue/issues/12726) [#12717](https://github.com/vuejs/vue/issues/12717)
-* **watch:** avoid pre watcher firing on unmount ([f0057b1](https://github.com/vuejs/vue/commit/f0057b101e6451d5095cdb7fd6308fd31ac0450c)), closes [#12703](https://github.com/vuejs/vue/issues/12703)
-
-
-### Features
-
-* **types:** enhance type for onErrorCaptured ([#12735](https://github.com/vuejs/vue/issues/12735)) ([bba6b3d](https://github.com/vuejs/vue/commit/bba6b3d6b4e3e26d28abbf20e74ec2f3e64f1a92))
-* **types:** export DefineComponent ([80d1baf](https://github.com/vuejs/vue/commit/80d1baf92050da411fb1bfe714401c498001dd36)), closes [#12748](https://github.com/vuejs/vue/issues/12748)
-* **types:** support mixins inference for new Vue() ([#12737](https://github.com/vuejs/vue/issues/12737)) ([89a6b5e](https://github.com/vuejs/vue/commit/89a6b5e8658a6e3ae2cf649829901784ac9deb3c)), closes [#12730](https://github.com/vuejs/vue/issues/12730)
-
-
-
-## [2.7.8](https://github.com/vuejs/vue/compare/v2.7.7...v2.7.8) (2022-07-22)
-
-
-### Bug Fixes
-
-* **reactivity:** fix shallowReactive nested ref setting edge cases ([2af751b](https://github.com/vuejs/vue/commit/2af751b6efa0cd9cb817ed96af41948e92599837)), closes [#12688](https://github.com/vuejs/vue/issues/12688)
-* **sfc:** align `<script setup>` component resolution edge case with v3 ([#12687](https://github.com/vuejs/vue/issues/12687)) ([a695c5a](https://github.com/vuejs/vue/commit/a695c5a6df415101a5f728e87dbe087b4fbb0b7b)), closes [#12685](https://github.com/vuejs/vue/issues/12685)
-* **types:** avoid circular type inference between v2 and v3 instance types ([fabc1cf](https://github.com/vuejs/vue/commit/fabc1cf89f55dd7f3f95fa7890d2540f40e9f845)), closes [#12683](https://github.com/vuejs/vue/issues/12683)
-* **types:** export `defineAsyncComponent` type ([#12684](https://github.com/vuejs/vue/issues/12684)) ([ba7dd2c](https://github.com/vuejs/vue/commit/ba7dd2c4ed62dba30bde7c1d521f7bfbab529102))
-
-
-### Features
-
-* **setup:** support listeners on setup context + `useListeners()` helper ([adf3ac8](https://github.com/vuejs/vue/commit/adf3ac8adcf204dcc34b851da6ab4d914bd11cf9))
-
-
-
-## [2.7.7](https://github.com/vuejs/vue/compare/v2.7.6...v2.7.7) (2022-07-16)
-
-
-### Bug Fixes
-
-* **codegen:** script setup should not attempt to resolve native elements as component ([e8d3a7d](https://github.com/vuejs/vue/commit/e8d3a7d7a17f9e66d592fb1ddc4a603af9958d36)), closes [#12674](https://github.com/vuejs/vue/issues/12674)
-* **inject:** fix edge case of provided with async-mutated getters ([ea5d0f3](https://github.com/vuejs/vue/commit/ea5d0f3fbfd983cb0275457cbcef344f926381ea)), closes [#12667](https://github.com/vuejs/vue/issues/12667)
-* **setup:** ensure setup context slots can be accessed immediately ([67760f8](https://github.com/vuejs/vue/commit/67760f8d30778f58afeada3589d4ac4493329ad5)), closes [#12672](https://github.com/vuejs/vue/issues/12672)
-* **types:** vue.d.ts should use relative import to v3-component-public-instance ([#12668](https://github.com/vuejs/vue/issues/12668)) ([46ec648](https://github.com/vuejs/vue/commit/46ec64869479393f95b6abda7a4adecf19867d06)), closes [#12666](https://github.com/vuejs/vue/issues/12666)
-* **watch:** fix queueing multiple post watchers ([25ffdb6](https://github.com/vuejs/vue/commit/25ffdb62d22fe8688aca144d945671d5c82a8887)), closes [#12664](https://github.com/vuejs/vue/issues/12664)
-
-
-
-## [2.7.6](https://github.com/vuejs/vue/compare/v2.7.5...v2.7.6) (2022-07-15)
-
-
-### Bug Fixes
-
-* **types:** $refs can also contain ComponentPublicInstance ([#12659](https://github.com/vuejs/vue/issues/12659)) ([fffbb9e](https://github.com/vuejs/vue/commit/fffbb9e856de5e4b3053a6b4d01d1404bfb6f85e))
-* **types:** fix $children and $root instance types ([52a5979](https://github.com/vuejs/vue/commit/52a59790a5b6e16c9f8cc737fcd784413dda282d)), closes [#12655](https://github.com/vuejs/vue/issues/12655)
-* **types:** fix missing expose() type on setup context ([e0a9546](https://github.com/vuejs/vue/commit/e0a9546ef32fa4bbfc4bede3002b2d6a5be8cf72)), closes [#12660](https://github.com/vuejs/vue/issues/12660)
-
-
-
-## [2.7.5](https://github.com/vuejs/vue/compare/v2.7.4...v2.7.5) (2022-07-13)
-
-
-### Bug Fixes
-
-* add missing export from `vue.runtime.mjs` ([#12648](https://github.com/vuejs/vue/issues/12648)) ([08fb4a2](https://github.com/vuejs/vue/commit/08fb4a222c016c79af77ab96817d2ed9b7abbd80))
-* detect property add/deletion on reactive objects from setup when used in templates ([a6e7498](https://github.com/vuejs/vue/commit/a6e74985cf2eab6f16d03a8eda3bf3fc7950127c))
-* do not set currentInstance in beforeCreate ([0825d30](https://github.com/vuejs/vue/commit/0825d3087f9f39435838329c16adc2a7bfccd51d)), closes [#12636](https://github.com/vuejs/vue/issues/12636)
-* **reactivity:** fix watch behavior inconsistency + deep ref shallow check ([98fb01c](https://github.com/vuejs/vue/commit/98fb01c79c41c3b9f9134f0abb77d233ce4e5b44)), closes [#12643](https://github.com/vuejs/vue/issues/12643)
-* **sfc:** fix sfc name inference type check ([04b4703](https://github.com/vuejs/vue/commit/04b4703de72b1c1e686a3aa81d5b5b56799dabab)), closes [#12637](https://github.com/vuejs/vue/issues/12637)
-* **types:** support Vue interface augmentations in defineComponent ([005e52d](https://github.com/vuejs/vue/commit/005e52d0b6f1a5bf9679789c5c4b90afd442d86b)), closes [#12642](https://github.com/vuejs/vue/issues/12642)
-* **watch:** fix deep watch for structures containing raw refs ([1a2c3c2](https://github.com/vuejs/vue/commit/1a2c3c2d77ba96ef496f4c86329b7798026511ae)), closes [#12652](https://github.com/vuejs/vue/issues/12652)
-
-
-
-## [2.7.4](https://github.com/vuejs/vue/compare/v2.7.3...v2.7.4) (2022-07-08)
-
-
-### Bug Fixes
-
-* **build:** fix mjs dual package hazard ([012e10c](https://github.com/vuejs/vue/commit/012e10c9ca13fcbc9bf67bf2835883edcd4faace)), closes [#12626](https://github.com/vuejs/vue/issues/12626)
-* **compiler-sfc:** use safer deindent default for compatibility with previous behavior ([b70a258](https://github.com/vuejs/vue/commit/b70a2585fcd102def2bb5a3b2b589edf5311122d))
-* pass element creation helper to static render fns for functional components ([dc8a68e](https://github.com/vuejs/vue/commit/dc8a68e8c6c4e8ed4fdde094004fca272d71ef2e)), closes [#12625](https://github.com/vuejs/vue/issues/12625)
-* **ssr/reactivity:** fix array setting error at created in ssr [[#12632](https://github.com/vuejs/vue/issues/12632)] ([#12633](https://github.com/vuejs/vue/issues/12633)) ([ca7daef](https://github.com/vuejs/vue/commit/ca7daefaa15a192046d22d060220cd595a6a275f))
-* **types:** fix missing instance properties on defineComponent this ([f8de4ca](https://github.com/vuejs/vue/commit/f8de4ca9d458a03378e848b1e62d6507f7124871)), closes [#12628](https://github.com/vuejs/vue/issues/12628#issuecomment-1177258223)
-* **types:** fix this.$slots type for defineComponent ([d3add06](https://github.com/vuejs/vue/commit/d3add06e6e18a78a3745240632fecd076eb49d19))
-* **types:** fix type inference when using components option ([1d5a411](https://github.com/vuejs/vue/commit/1d5a411c1e3aa062aa5080432cf3f852f1583ed2))
-* **types:** global component registration type compat w/ defineComponent ([26ff4bc](https://github.com/vuejs/vue/commit/26ff4bc0ed75d8bf7921523a2e546df24ec81d8f)), closes [#12622](https://github.com/vuejs/vue/issues/12622)
-* **watch:** fix watchers triggered in mounted hook ([8904ca7](https://github.com/vuejs/vue/commit/8904ca77c2d675707728e6a50decd3ef3370a428)), closes [#12624](https://github.com/vuejs/vue/issues/12624)
-
-
-### Features
-
-* defineAsyncComponent ([9d12106](https://github.com/vuejs/vue/commit/9d12106e211e0cbf33f9066606a8ff29f8cc8e8d)), closes [#12608](https://github.com/vuejs/vue/issues/12608)
-* support functional components in defineComponent ([559600f](https://github.com/vuejs/vue/commit/559600f13d312915c0a1b54ed4edd41327dbedd6)), closes [#12619](https://github.com/vuejs/vue/issues/12619)
-
-
-
-## [2.7.3](https://github.com/vuejs/vue/compare/v2.7.2...v2.7.3) (2022-07-06)
-
-
-### Bug Fixes
-
-* add renderTracked/Triggered merge strategy ([#12616](https://github.com/vuejs/vue/issues/12616)) ([6d1dbee](https://github.com/vuejs/vue/commit/6d1dbeee7ebd27a87c37393d8cabf2ef8253f3a4))
-* **ssr/reactivity:** fix composition api behavior in SSR ([360272b](https://github.com/vuejs/vue/commit/360272bde32e93b1a8d611e4b97af1300c38e7a3)), closes [#12615](https://github.com/vuejs/vue/issues/12615)
-* **types:** allow slot attribute ([94ccca2](https://github.com/vuejs/vue/commit/94ccca207c9c5fc0fc8985e0c103d8a1c9cff412)), closes [#12617](https://github.com/vuejs/vue/issues/12617)
-
-
-
-## [2.7.2](https://github.com/vuejs/vue/compare/v2.7.1...v2.7.2) (2022-07-05)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** preserve old deindent behavior for pug ([1294385](https://github.com/vuejs/vue/commit/1294385300742acbea5f05c166685bd4a1fab35a)), closes [#12611](https://github.com/vuejs/vue/issues/12611)
-
-
-### Features
-
-* allow passing directive definition directly to h() ([#12590](https://github.com/vuejs/vue/issues/12590)) ([d45bbea](https://github.com/vuejs/vue/commit/d45bbeac2710a5cf9185377abc2342295f0bb4e2))
-* **types:** define component improvements ([#12612](https://github.com/vuejs/vue/issues/12612)) ([fb93c1b](https://github.com/vuejs/vue/commit/fb93c1be77f12ea1375c5e8b47d168e4d5ceb7be))
-
-
-
-## [2.7.1](https://github.com/vuejs/vue/compare/v2.7.0...v2.7.1) (2022-07-04)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** fix template usage check edge case for v-on statements ([caceece](https://github.com/vuejs/vue/commit/caceece8fa9b75c4832a91d3af0bc2958166d3f2)), closes [#12591](https://github.com/vuejs/vue/issues/12591)
-* export proxyRefs ([e452e9d](https://github.com/vuejs/vue/commit/e452e9d4368aaba173e8c942e5fd82f999cf65ae)), closes [#12600](https://github.com/vuejs/vue/issues/12600)
-* fix NaN comparison on state change ([ff5acb1](https://github.com/vuejs/vue/commit/ff5acb12cfa64f975f216a55aafa976b07052665)), closes [#12595](https://github.com/vuejs/vue/issues/12595)
-* **reactivity:**  shallowReactive should not unwrap refs ([#12605](https://github.com/vuejs/vue/issues/12605)) ([15b9b9b](https://github.com/vuejs/vue/commit/15b9b9b19dd3ae7cce5dcd89739e5cf719523fc5)), closes [#12597](https://github.com/vuejs/vue/issues/12597)
-
-
-
-# [2.7.0](https://github.com/vuejs/vue/compare/v2.7.0-beta.8...v2.7.0) (2022-07-01)
-
-## Backported Features
-
-- [Composition API](https://vuejs.org/guide/extras/composition-api-faq.html)
-- SFC [`<script setup>`](https://vuejs.org/api/sfc-script-setup.html)
-- SFC [CSS v-bind](https://vuejs.org/api/sfc-css-features.html#v-bind-in-css)
-
-In addition, the following APIs are also supported:
-
-- `defineComponent()` with improved type inference (compared to `Vue.extend`)
-- `h()`, `useSlot()`, `useAttrs()`, `useCssModules()`
-- `set()`, `del()` and `nextTick()` are also provided as named exports in ESM builds.
-- The `emits` option is also supported, but only for type-checking purposes (does not affect runtime behavior)
-
-  2.7 also supports using ESNext syntax in template expressions. When using a build system, the compiled template render function will go through the same loaders / plugins configured for normal JavaScript. This means if you have configured Babel for `.js` files, it will also apply to the expressions in your SFC templates.
-
-### Notes on API exposure
-
-- In ESM builds, these APIs are provided as named exports (and named exports only):
-
-  ```js
-  import Vue, { ref } from 'vue'
-
-  Vue.ref // undefined, use named export instead
-  ```
-
-- In UMD and CJS builds, these APIs are exposed as properties on the global `Vue` object.
-
-- When bundling with CJS builds externalized, bundlers should be able to handle ESM interop when externalizing CJS builds.
-
-### Behavior Differences from Vue 3
-
-The Composition API is backported using Vue 2's getter/setter-based reactivity system to ensure browser compatibility. This means there are some important behavior differences from Vue 3's proxy-based system:
-
-- All [Vue 2 change detection caveats](https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats) still apply.
-
-- `reactive()`, `ref()`, and `shallowReactive()` will directly convert original objects instead of creating proxies. This means:
-
-  ```js
-  // true in 2.7, false in 3.x
-  reactive(foo) === foo
-  ```
-
-- `readonly()` **does** create a separate object, but it won't track newly added properties and does not work on arrays.
-
-- Avoid using arrays as root values in `reactive()` because without property access the array's mutation won't be tracked (this will result in a warning).
-
-- Reactivity APIs ignore properties with symbol keys.
-
-In addition, the following features are explicitly **NOT** ported:
-
-- ❌ `createApp()` (Vue 2 doesn't have isolated app scope)
-- ❌ Top-level `await` in `<script setup>` (Vue 2 does not support async component initialization)
-- ❌ TypeScript syntax in template expressions (incompatible w/ Vue 2 parser)
-- ❌ Reactivity transform (still experimental)
-- ❌ `expose` option is not supported for options components (but `defineExpose()` is supported in `<script setup>`).
-
-### TypeScript Changes
-
-- `defineComponent` provides improved type inference similar to that of Vue 3. Note the type of `this` inside `defineComponent()` is not interoperable with `this` from `Vue.extend()`.
-
-- Similar to Vue 3, TSX support is now built-in. If your project previously had manual JSX type shims, make sure to remove them.
-
-## Upgrade Guide
-
-### Vue CLI / webpack
-
-1. Upgrade local `@vue/cli-xxx` dependencies the latest version in your major version range (if applicable):
-
-   - `~4.5.18` for v4
-   - `~5.0.6` for v5
-
-2. Upgrade `vue` to `^2.7.0`. You can also remove `vue-template-compiler` from the dependencies - it is no longer needed in 2.7.
-
-   Note: if you are using `@vue/test-utils`, you may need to keep it in the dependencies for now, but this requirement will also be lifted in a new release of test utils.
-
-3. Check your package manager lockfile to ensure the following dependencies meet the version requirements. They may be transitive dependencies not listed in `package.json`.
-
-   - `vue-loader`: `^15.10.0`
-   - `vue-demi`: `^0.13.1`
-
-   If not, you will need to remove `node_modules` and the lockfile and perform a fresh install to ensure they are bumped to the latest version.
-
-4. If you were previously using [`@vue/composition-api`](https://github.com/vuejs/composition-api), update imports from it to `vue` instead. Note that some APIs exported by the plugin, e.g. `createApp`, are not ported in 2.7.
-
-5. Update `eslint-plugin-vue` to latest version (9+) if you run into unused variable lint errors when using `<script setup>`.
-
-6. The SFC compiler for 2.7 now uses PostCSS 8 (upgraded from 7). PostCSS 8 should be backwards compatible with most plugins, but the upgrade **may** cause issues if you were previously using a custom PostCSS plugin that can only work with PostCSS 7. In such cases, you will need to upgrade the relevant plugins to their PostCSS 8 compatible versions.
-
-### Vite
-
-2.7 support for Vite is provided via a new plugin: [@vitejs/plugin-vue2](https://github.com/vitejs/vite-plugin-vue2). This new plugin requires Vue 2.7 or above and supersedes the existing [vite-plugin-vue2](https://github.com/underfin/vite-plugin-vue2).
-
-Note that the new plugin does not handle Vue-specific JSX / TSX transform, which is intentional. Vue 2 JSX / TSX transform should be handled in a separate, dedicated plugin, which will be provided soon.
-
-### Volar Compatibility
-
-2.7 ships improved type definitions so it is no longer necessary to install `@vue/runtime-dom` just for Volar template type inference support. All you need now is the following config in `tsconfig.json`:
-
-```json
-{
-  // ...
-  "vueCompilerOptions": {
-    "target": 2.7
-  }
-}
-```
-
-### Devtools Support
-
-Vue Devtools 6.2.0 has added support for inspecting 2.7 Composition API state, but the extensions may still need a few days to go through review on respective publishing platforms.
-
-### Bug Fixes
-
-* **sfc:** only include legacy decorator parser plugin when new plugin is not used ([326d24a](https://github.com/vuejs/vue/commit/326d24a4e42b4f4fd243b36037e88b71963d963d))
-
-
-
-# [2.7.0-beta.8](https://github.com/vuejs/vue/compare/v2.7.0-beta.7...v2.7.0-beta.8) (2022-06-28)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** should transform non relative paths when base option is present ([008d78b](https://github.com/vuejs/vue/commit/008d78bd7280716d2d70e150e3ed2ac5ee8497f2))
-
-
-
-# [2.7.0-beta.7](https://github.com/vuejs/vue/compare/v2.7.0-beta.6...v2.7.0-beta.7) (2022-06-27)
-
-
-### Bug Fixes
-
-* types/v3-directive not published ([#12571](https://github.com/vuejs/vue/issues/12571)) ([11e4bfe](https://github.com/vuejs/vue/commit/11e4bfe806b574747495edfb9ba22f78676ceb7c))
-
-
-
-# [2.7.0-beta.6](https://github.com/vuejs/vue/compare/v2.7.0-beta.5...v2.7.0-beta.6) (2022-06-26)
-
-
-### Bug Fixes
-
-* **reactivity:** readonly() compat with classes ([44ab1cd](https://github.com/vuejs/vue/commit/44ab1cda746252a1a81f88e1a9658d33f484ed41)), closes [#12574](https://github.com/vuejs/vue/issues/12574)
-* **watch:** template ref watcher should fire after owner instance mounted hook ([089b27c](https://github.com/vuejs/vue/commit/089b27c30b470acd98286ffe072525beb2320dea)), closes [#12578](https://github.com/vuejs/vue/issues/12578)
-
-
-
-# [2.7.0-beta.5](https://github.com/vuejs/vue/compare/v2.7.0-beta.4...v2.7.0-beta.5) (2022-06-22)
-
-
-### Bug Fixes
-
-* **types:** fix instance type inference for setup() with no return value ([65531f5](https://github.com/vuejs/vue/commit/65531f580314d832f44ecd5c782f79da3e977da7)), closes [#12568](https://github.com/vuejs/vue/issues/12568)
-* **watch:** fix pre watchers not flushed on mount for nested component ([7a3aa3f](https://github.com/vuejs/vue/commit/7a3aa3faac6b8eb066f32813788b4c1d16f34c6b)), closes [#12569](https://github.com/vuejs/vue/issues/12569)
-
-
-
-# [2.7.0-beta.4](https://github.com/vuejs/vue/compare/v2.7.0-beta.3...v2.7.0-beta.4) (2022-06-21)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** properly handle shorthand property in template expressions ([9b9f2bf](https://github.com/vuejs/vue/commit/9b9f2bf1c8429136e7122d146e0e39bdeada5d1d)), closes [#12566](https://github.com/vuejs/vue/issues/12566)
-
-
-
-# [2.7.0-beta.3](https://github.com/vuejs/vue/compare/v2.7.0-beta.2...v2.7.0-beta.3) (2022-06-20)
-
-
-### Bug Fixes
-
-* remove wrong observe toggle, properly disable tracking in setup() ([2d67641](https://github.com/vuejs/vue/commit/2d676416566c06d7c789899c92bf0f6924ec284a))
-* **setup:** setup props should pass isReactive check ([52cf912](https://github.com/vuejs/vue/commit/52cf912d855a7fae8d8c89452f0d275846e26a87)), closes [#12561](https://github.com/vuejs/vue/issues/12561)
-* **template-ref:** preserve ref removal behavior in non-composition-api usage ([2533a36](https://github.com/vuejs/vue/commit/2533a360a838c41238dc9b9b840932f2957c65d4)), closes [#12554](https://github.com/vuejs/vue/issues/12554)
-
-
-### Features
-
-* **sfc:** css v-bind ([8ab0074](https://github.com/vuejs/vue/commit/8ab0074bab83473fd025ae538a3455e3277320c4))
-* **sfc:** parse needMap compat ([d3916b6](https://github.com/vuejs/vue/commit/d3916b69b491bb0964102b69a8bb2e9336f7677b))
-* useCssModules ([0fabda7](https://github.com/vuejs/vue/commit/0fabda7a3b016c1c0cb5d92f5d8efc35f0dbde40))
-
-
-
-# [2.7.0-beta.2](https://github.com/vuejs/vue/compare/v2.7.0-beta.1...v2.7.0-beta.2) (2022-06-17)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** do not transform external and data urls in templates ([328ebff](https://github.com/vuejs/vue/commit/328ebff04198143385371c857ad23f739be9509d))
-
-
-
-# [2.7.0-beta.1](https://github.com/vuejs/vue/compare/v2.7.0-alpha.12...v2.7.0-beta.1) (2022-06-17)
-
-
-### Bug Fixes
-
-* **compiler-sfc:** expose src on custom blocks as well ([cdfc4c1](https://github.com/vuejs/vue/commit/cdfc4c134ddadc33f3b50d53ec6316b0c96f4567))
-
-
-### Features
-
-* **compiler-sfc:** support includeAbsolute in transformAssetUrl ([8f5817a](https://github.com/vuejs/vue/commit/8f5817a7c9a0664438e4299f82ac41a67f156f89))
-* warn top level await usage in `<script setup>` ([efa8a74](https://github.com/vuejs/vue/commit/efa8a749644cfd6a0d6e9e92a1f342e2eff77d5a))
-
-
-
-# [2.7.0-alpha.12](https://github.com/vuejs/vue/compare/v2.7.0-alpha.11...v2.7.0-alpha.12) (2022-06-16)
-
-
-### Features
-
-* further align compiler-sfc api + expose rewriteDefault ([8ce585d](https://github.com/vuejs/vue/commit/8ce585d70c2e1adb04650801b03dcc9552cbaf95))
-
-
-
-# [2.7.0-alpha.11](https://github.com/vuejs/vue/compare/v2.7.0-alpha.10...v2.7.0-alpha.11) (2022-06-16)
-
-
-### Bug Fixes
-
-* further align types with v3 ([2726b6d](https://github.com/vuejs/vue/commit/2726b6d9ff3030af63012a304c33781163461a23))
-
-
-
-# [2.7.0-alpha.10](https://github.com/vuejs/vue/compare/v2.7.0-alpha.9...v2.7.0-alpha.10) (2022-06-16)
-
-
-### Bug Fixes
-
-* avoid warning when accessing _setupProxy ([cdfd9f3](https://github.com/vuejs/vue/commit/cdfd9f321e35981774785806bb1629a73ec58064))
-
-
-### Features
-
-* export version as named export ([749b96d](https://github.com/vuejs/vue/commit/749b96d84e6551c5187694a93c5493702035a239))
-
-
-
-# [2.7.0-alpha.9](https://github.com/vuejs/vue/compare/v2.7.0-alpha.8...v2.7.0-alpha.9) (2022-06-16)
-
-
-### Features
-
-* defineExpose() support ([3c2707b](https://github.com/vuejs/vue/commit/3c2707b62e1a355f182c08f85cf7bc9bca1bb34e))
-* directive resolution for `<script setup>` ([aa2b1f4](https://github.com/vuejs/vue/commit/aa2b1f4d93bdd257ae26a73994168709369aa6a0))
-* resolve components from `<script setup>` ([4b19339](https://github.com/vuejs/vue/commit/4b193390fbc203cc5bae46fe288b1c20c442cc03))
-* types for `<script setup>` macros ([7173ad4](https://github.com/vuejs/vue/commit/7173ad42729fa0913c3e7de94d251341929ee7c6))
-
-
-
-# [2.7.0-alpha.8](https://github.com/vuejs/vue/compare/v2.7.0-alpha.7...v2.7.0-alpha.8) (2022-06-14)
-
-### Features
-
-- Basic `<script setup>` support. Requires `vue-loader@^15.10.0-beta.1`.
-  - Component resolution doesn't work yet
-  - Types for `defineXXX` macros not supported yet
-
-# [2.7.0-alpha.7](https://github.com/vuejs/vue/compare/v2.7.0-alpha.6...v2.7.0-alpha.7) (2022-06-14)
-
-
-
-# [2.7.0-alpha.6](https://github.com/vuejs/vue/compare/v2.7.0-alpha.5...v2.7.0-alpha.6) (2022-06-09)
-
-### Features
-
-* Add JSX types for Volar integration. No longer requires `@vue/runtime-dom` for component props validation in templates.
-
-
-# [2.7.0-alpha.5](https://github.com/vuejs/vue/compare/v2.7.0-alpha.4...v2.7.0-alpha.5) (2022-06-06)
-
-
-### Bug Fixes
-
-* fix scopedSlots regression ([4f2a04e](https://github.com/vuejs/vue/commit/4f2a04e6a8e1fc71080eed49fd4059d6a53afc1c))
-
-
-
-# [2.7.0-alpha.4](https://github.com/vuejs/vue/compare/v2.7.0-alpha.3...v2.7.0-alpha.4) (2022-06-01)
-
-
-### Bug Fixes
-
-* guard against non-object provide value ([c319cc7](https://github.com/vuejs/vue/commit/c319cc7a74a790414c33db74ad9f1070851de76b))
-
-### Features
-
-* `defineComponent` [206f8a7f](https://github.com/vuejs/vue/commit/206f8a7f0949a50ae062d9d71061716ae3c3a749)
-
-# [2.7.0-alpha.3](https://github.com/vuejs/vue/compare/v2.7.0-alpha.2...v2.7.0-alpha.3) (2022-06-01)
-
-
-
-# [2.7.0-alpha.2](https://github.com/vuejs/vue/compare/v2.7.0-alpha.1...v2.7.0-alpha.2) (2022-06-01)
-
-
-### Features
-
-* add exports field + mjs build ([d317237](https://github.com/vuejs/vue/commit/d3172377e0c2df9e84244ede6fc9a091344e6f1c))
-* expose set/del as named exports ([5673363](https://github.com/vuejs/vue/commit/5673363eda9e6082a9ff0300b8c4e79c9388891b))
-
-
-
-# [2.7.0-alpha.1](https://github.com/vuejs/vue/compare/v2.6.14...v2.7.0-alpha.1) (2022-05-31)
-
-This release includes full [Composition API](https://vuejs.org/guide/extras/composition-api-faq.html) support, including:
-
-- `setup()` support in components
-- Reactivity APIs (`ref()`, `reactive()` etc.)
-- Lifecycle hooks (`onMounted()` etc.)
-- `provide()` and `inject()`
-- `useSlots()` and `useAttrs()`
-- template refs with `setup()`
-
-### Behavior difference from Vue 3
-
-- The reactivity system is still getter/setter based and does not use Proxies, so all Vue 2 change detection caveats still apply.
-- `reactive()`, `ref()`, and `shallowReactive()` will directly convert original objects instead of creating proxies. They also do not convert properties with symbol keys.
-- Avoid using arrays as root values in `reactive()` because without property access the array's mutation won't be tracked (this will result in a warning).
-- `readonly()` **does** create a separate object, but it won't track newly added properties and does not work on arrays.
-
-### Notes on API exposure
-
-- In ESM builds, these APIs are provided as named exports (and named exports only):
-
-  ```js
-  import Vue, { ref } from 'vue'
-
-  Vue.ref // undefined, use named export instead
-  ```
-
-- When bundling with CJS builds externalized, bundlers should be able to handle ESM interop when externalizing CJS builds.
-
-- In UMD builds, these APIs are exposed on the global `Vue` object.
-
-In addition:
-
-- `h()`, `set()`, `del()` and `nextTick()` are now also provided as named exports in ESM builds.
-
-### Bug Fixes
-
-- **v-on:** add removing all dom event listeners when vnode destroyed ([#10085](https://github.com/vuejs/vue/issues/10085)) ([3d29ba8](https://github.com/vuejs/vue/commit/3d29ba863b89fd90dabd0856c0507eacdf5fef22))
-
-### Features
-
-- **compiler:** condenses staticClass whitespace (fix [#12113](https://github.com/vuejs/vue/issues/12113)) ([#12195](https://github.com/vuejs/vue/issues/12195)) ([515467a](https://github.com/vuejs/vue/commit/515467a618479792abedf01a7b1dcef2ac2a17ed))
-
-## [2.6.14](https://github.com/vuejs/vue/compare/v2.6.13...v2.6.14) (2021-06-07)
-
-### Bug Fixes
-
-- **types:** async Component types ([#11906](https://github.com/vuejs/vue/issues/11906)) ([c52427b](https://github.com/vuejs/vue/commit/c52427b0d2c1d203deea6eb69f2b4b181d56022c))
-- **v-slot:** fix scoped slot normalization combined with v-if ([#12104](https://github.com/vuejs/vue/issues/12104)) ([38f71de](https://github.com/vuejs/vue/commit/38f71de380d566e4eef60968a8eca6bd6f482dd5))
-
-### Features
-
-- **ssr:** vue-ssr-webpack-plugin compatible with webpack 5 ([#12002](https://github.com/vuejs/vue/issues/12002)) ([80e7730](https://github.com/vuejs/vue/commit/80e7730946538e0371e213100a0fe81299c2f4b2))
-
-## [2.6.13](https://github.com/vuejs/vue/compare/v2.6.12...v2.6.13) (2021-06-01)
-
-### Bug Fixes
-
-- **attrs:** do not consider translate attribute as boolean ([#11392](https://github.com/vuejs/vue/issues/11392)) ([cd57393](https://github.com/vuejs/vue/commit/cd57393fd3e2c169d450607bc4f03652d106bcc2)), closes [#11391](https://github.com/vuejs/vue/issues/11391)
-- **compiler:** Allow BigInt usage in templates ([#11152](https://github.com/vuejs/vue/issues/11152)) ([c42b706](https://github.com/vuejs/vue/commit/c42b7066cae7947e9fd877e495aeb38623c2354d))
-- **compiler:** avoid converting &nbps; to spaces ([#11065](https://github.com/vuejs/vue/issues/11065)) ([55a30cf](https://github.com/vuejs/vue/commit/55a30cf9db247eba2aca817439fdb3cd15e9184f))
-- **compiler:** event handlers with modifiers swallowing arguments (fix [#10867](https://github.com/vuejs/vue/issues/10867)) ([#10958](https://github.com/vuejs/vue/issues/10958)) ([8620706](https://github.com/vuejs/vue/commit/862070662dd4871cb834664435ec836df57c7d57))
-- **core:** fix sameVnode for async component ([#11107](https://github.com/vuejs/vue/issues/11107)) ([5260830](https://github.com/vuejs/vue/commit/52608302e9bca84fb9e9f0499e89acade78d3d07))
-- **core:** remove trailing comma in function signature ([#10845](https://github.com/vuejs/vue/issues/10845)) ([579e1ff](https://github.com/vuejs/vue/commit/579e1ff9df1d454f85fac386d098b7bf1a42c4f2)), closes [#10843](https://github.com/vuejs/vue/issues/10843)
-- **errorHandler:** async error handling for watchers ([#9484](https://github.com/vuejs/vue/issues/9484)) ([e4dea59](https://github.com/vuejs/vue/commit/e4dea59f84dfbf32cda1cdd832380dd90b1a6fd1))
-- force update between two components with and without slot ([#11795](https://github.com/vuejs/vue/issues/11795)) ([77b5330](https://github.com/vuejs/vue/commit/77b5330c5498a6b14a83197371e9a2dbf9939a9c))
-- give correct namespace in foreignObject ([#11576](https://github.com/vuejs/vue/issues/11576)) ([af5e05d](https://github.com/vuejs/vue/commit/af5e05d87ecd218f73302a1b08dcaedd2b46814a)), closes [#11575](https://github.com/vuejs/vue/issues/11575)
-- handle async placeholders in normalizeScopedSlot ([#11963](https://github.com/vuejs/vue/issues/11963)) ([af54514](https://github.com/vuejs/vue/commit/af54514cf97e724d224408c1ecc6c81ddccd4b75))
-- **keep-alive:** cache what is really needed not the whole VNode data ([#12015](https://github.com/vuejs/vue/issues/12015)) ([e7baaa1](https://github.com/vuejs/vue/commit/e7baaa12055231c9367fa1c7bf917e534bd8a739))
-- **parser:** allow multiple slots with new syntax ([#9785](https://github.com/vuejs/vue/issues/9785)) ([67825c2](https://github.com/vuejs/vue/commit/67825c24bcb0a9f64055bda1b1e4af66aad3c529)), closes [#9781](https://github.com/vuejs/vue/issues/9781)
-- pause dep collection during immediate watcher invocation ([#11943](https://github.com/vuejs/vue/issues/11943)) ([987f322](https://github.com/vuejs/vue/commit/987f322b8f419cc307f4294173f8792a706ed73f))
-- **props:** correctly warn when a provided prop is Symbol ([#10529](https://github.com/vuejs/vue/issues/10529)) ([abb5ef3](https://github.com/vuejs/vue/commit/abb5ef35dd02919dce19c895ad12113071712df0)), closes [#10519](https://github.com/vuejs/vue/issues/10519)
-- **props:** support BigInt in props type validation ([#11191](https://github.com/vuejs/vue/issues/11191)) ([fa1f81e](https://github.com/vuejs/vue/commit/fa1f81e91062e9de6161708209cd7354733aa354))
-- **slot:** add a function to return the slot fallback content ([#12014](https://github.com/vuejs/vue/issues/12014)) ([ce457f9](https://github.com/vuejs/vue/commit/ce457f9f4d48548d5e8763c47d013e23c2b65c12))
-- **ssr:** avoid missing files in manifest ([#11609](https://github.com/vuejs/vue/issues/11609)) ([b97606c](https://github.com/vuejs/vue/commit/b97606cdc658448b56518ac27af98fc82999d05f))
-- **ssr:** inheritAttrs false adds attributes to html ([#11706](https://github.com/vuejs/vue/issues/11706)) ([7e5dc6b](https://github.com/vuejs/vue/commit/7e5dc6bd9ebc1620624191804d2ace43cae557a8))
-- **ssr:** textarea keeps undefined/null values ([#11121](https://github.com/vuejs/vue/issues/11121)) ([b8bd149](https://github.com/vuejs/vue/commit/b8bd149d8aa3f175a1a656d62f7b6ec60c31a364))
-- **types:** add types for Vue.util.warn function ([#11964](https://github.com/vuejs/vue/issues/11964)) ([e0274e4](https://github.com/vuejs/vue/commit/e0274e4320f68bb93bd7f90bb1ef701ccf9b6f2a)), closes [/github.com/vuejs/vue/blob/v2.6.12/src/core/util/debug.js#L18-L26](https://github.com//github.com/vuejs/vue/blob/v2.6.12/src/core/util/debug.js/issues/L18-L26)
-- **types:** allow string for watch handlers in options ([#10396](https://github.com/vuejs/vue/issues/10396)) ([668e1e6](https://github.com/vuejs/vue/commit/668e1e637461ff630803e85bf99158415d276d4c))
-- **types:** allow symbol & boolean for vnode key ([#11914](https://github.com/vuejs/vue/issues/11914)) ([5c459f0](https://github.com/vuejs/vue/commit/5c459f0fd6911daca09ad205aecf5423a9d05698))
-- **types:** changed expression type to optional string ([#11189](https://github.com/vuejs/vue/issues/11189)) ([7c75462](https://github.com/vuejs/vue/commit/7c754623541c492161f7976203f0b1697a9a0113)), closes [#10871](https://github.com/vuejs/vue/issues/10871)
-- **types:** make $refs undefined possible ([#11112](https://github.com/vuejs/vue/issues/11112)) ([2b93e86](https://github.com/vuejs/vue/commit/2b93e86aa1437168476cbb5100cfb3bbbac55efa))
-- **v-on:** avoid events with empty keyCode (autocomplete) ([#11326](https://github.com/vuejs/vue/issues/11326)) ([c6d7a6f](https://github.com/vuejs/vue/commit/c6d7a6fce795ffbd6b8a599787eca986bb260a25))
-- **v-pre:** do not alter attributes ([#10088](https://github.com/vuejs/vue/issues/10088)) ([0664cb0](https://github.com/vuejs/vue/commit/0664cb01434f3d52efd076b6aafe54066a2a762a)), closes [#10087](https://github.com/vuejs/vue/issues/10087)
-- **vdom:** avoid executing root level script tags ([#11487](https://github.com/vuejs/vue/issues/11487)) ([fb16d7b](https://github.com/vuejs/vue/commit/fb16d7bfa1e32c21a2f4b627fb8864d3c5c6b655)), closes [#11483](https://github.com/vuejs/vue/issues/11483)
-- **warn:** better message with no constructors props ([#9241](https://github.com/vuejs/vue/issues/9241)) ([6940131](https://github.com/vuejs/vue/commit/69401311f4bf55e58550a2134c33ceb8ae1f180e))
-- **warns:** modify `maybeComponent` function in parser ([#10167](https://github.com/vuejs/vue/issues/10167)) ([0603ff6](https://github.com/vuejs/vue/commit/0603ff695d2f41286239298210113cbe2b209e28)), closes [#10152](https://github.com/vuejs/vue/issues/10152)
-
-### Features
-
-- **warns:** avoid warning native modifiers on dynamic components ([#11052](https://github.com/vuejs/vue/issues/11052)) ([3d46692](https://github.com/vuejs/vue/commit/3d46692ee4e8ec67b5bc0f66cdabf4667fa4de88))
-- **warn:** warn computed conflict with methods ([#10119](https://github.com/vuejs/vue/issues/10119)) ([3ad60fe](https://github.com/vuejs/vue/commit/3ad60fea73d042fc9a127d19de8329948d3f2ef0))
-
-### Performance Improvements
-
-- preinitialize typeCheck RegExp ([#10990](https://github.com/vuejs/vue/issues/10990)) ([2488a6a](https://github.com/vuejs/vue/commit/2488a6a1e9779f0cca4a64163ef44ac30530a450))
-
-## [2.6.12](https://github.com/vuejs/vue/compare/v2.6.11...v2.6.12) (2020-08-20)
-
-### Bug Fixes
-
-- **security:** upgrade serialize-javascript ([#11434](https://github.com/vuejs/vue/issues/11434)) ([5b39961](https://github.com/vuejs/vue/commit/5b399612d8323ad0bb8b3f6fa8b2982ab73c0e6e))
-
-## [2.6.11](https://github.com/vuejs/vue/compare/v2.6.10...v2.6.11) (2019-12-13)
-
-### Bug Fixes
-
-- **compiler:** Remove the warning for valid v-slot value ([#9917](https://github.com/vuejs/vue/issues/9917)) ([085d188](https://github.com/vuejs/vue/commit/085d188379af98e9f482d7e2009ebfd771bd7ca5))
-- fix function expression regex ([#9922](https://github.com/vuejs/vue/issues/9922)) ([569b728](https://github.com/vuejs/vue/commit/569b728ab19d1956bf935a98c9c65a03d92ac85f)), closes [#9920](https://github.com/vuejs/vue/issues/9920)
-- **types:** fix global namespace declaration for UMD bundle ([#9912](https://github.com/vuejs/vue/issues/9912)) ([ab50e8e](https://github.com/vuejs/vue/commit/ab50e8e1da2f4f944af683252481728485fedf16))
-- **types:** fix prop constructor type inference ([#10779](https://github.com/vuejs/vue/issues/10779)) ([4821149](https://github.com/vuejs/vue/commit/4821149b8bbd4650b1d9c9c3cfbb539ac1e24589))
-
-## [2.6.10](https://github.com/vuejs/vue/compare/v2.6.9...v2.6.10) (2019-03-20)
-
-### Bug Fixes
-
-- **codegen:** support named function expression in v-on ([#9709](https://github.com/vuejs/vue/issues/9709)) ([3433ba5](https://github.com/vuejs/vue/commit/3433ba5beef9a6dd97705943c3441ebbee222afd)), closes [#9707](https://github.com/vuejs/vue/issues/9707)
-- **core:** cleanup timeouts for async components ([#9649](https://github.com/vuejs/vue/issues/9649)) ([02d21c2](https://github.com/vuejs/vue/commit/02d21c265c239682e73b2b3f98028f2da5e7205d)), closes [#9648](https://github.com/vuejs/vue/issues/9648)
-- **core:** only unset dom prop when not present ([f11449d](https://github.com/vuejs/vue/commit/f11449d916a468651d4fd5024c37e3eebbc9941f)), closes [#9650](https://github.com/vuejs/vue/issues/9650)
-- **core:** use window.performance for compatibility in JSDOM ([#9700](https://github.com/vuejs/vue/issues/9700)) ([653c74e](https://github.com/vuejs/vue/commit/653c74e64e5ccd66cda94c77577984f8afa8386d)), closes [#9698](https://github.com/vuejs/vue/issues/9698)
-- **scheduler:** revert timeStamp check ([22790b2](https://github.com/vuejs/vue/commit/22790b250cd5239a8379b4ec8cc3a9b570dac4bc)), closes [#9729](https://github.com/vuejs/vue/issues/9729) [#9632](https://github.com/vuejs/vue/issues/9632)
-- **slots:** fix slots not updating when passing down normal slots as $scopedSlots ([ebc1893](https://github.com/vuejs/vue/commit/ebc1893faccd1a9d953a8e8feddcb49cf1b9004d)), closes [#9699](https://github.com/vuejs/vue/issues/9699)
-- **types:** allow using functions on the PropTypes ([#9733](https://github.com/vuejs/vue/issues/9733)) ([df4af4b](https://github.com/vuejs/vue/commit/df4af4bd1906b9f23b62816142fdfbd6336d3d2f)), closes [#9692](https://github.com/vuejs/vue/issues/9692)
-- **types:** support string type for style in VNode data ([#9728](https://github.com/vuejs/vue/issues/9728)) ([982d5a4](https://github.com/vuejs/vue/commit/982d5a492fb95577217e2dacaa044eabe78a8601)), closes [#9727](https://github.com/vuejs/vue/issues/9727)
-
-## [2.6.9](https://github.com/vuejs/vue/compare/v2.6.8...v2.6.9) (2019-03-14)
-
-### Bug Fixes
-
-- **compiler:** whitespace: 'condense' should honor pre tag as well ([#9660](https://github.com/vuejs/vue/issues/9660)) ([f1bdd7f](https://github.com/vuejs/vue/commit/f1bdd7ff9d1fc86f7a8ad8d5cb6d9abc7b2e47f3))
-- event timeStamp check for Qt ([7591b9d](https://github.com/vuejs/vue/commit/7591b9dc6dde314f2d32dcd7a8355f696a330979)), closes [#9681](https://github.com/vuejs/vue/issues/9681)
-- **scheduler:** fix getNow check in IE9 ([#9647](https://github.com/vuejs/vue/issues/9647)) ([da77d6a](https://github.com/vuejs/vue/commit/da77d6a98bdccd8a2c8bfdfe6b9cb46efcb1193c)), closes [#9632](https://github.com/vuejs/vue/issues/9632)
-- **scheduler:** getNow detection can randomly fail ([#9667](https://github.com/vuejs/vue/issues/9667)) ([ef2a380](https://github.com/vuejs/vue/commit/ef2a380c6eb6bd1a7ff516c357dafa717e75a745))
-- should consider presence of normal slots when caching normalized scoped slots ([9313cf9](https://github.com/vuejs/vue/commit/9313cf91740e1d43c43cf9e73d905dbab913beb5)), closes [#9644](https://github.com/vuejs/vue/issues/9644)
-- should not swallow user catch on rejected promise in methods ([7186940](https://github.com/vuejs/vue/commit/7186940143704acc4ec046132f6a56e9c983e510)), closes [#9694](https://github.com/vuejs/vue/issues/9694)
-- should use fallback for scoped slots with single falsy v-if ([781c705](https://github.com/vuejs/vue/commit/781c70514e01bc402828946805bfad7437c7175e)), closes [#9658](https://github.com/vuejs/vue/issues/9658)
-- **ssr:** fix nested async functional component rendering ([#9673](https://github.com/vuejs/vue/issues/9673)) ([8082d2f](https://github.com/vuejs/vue/commit/8082d2f910d963f14c151fb445e0fcc5c975cca9)), closes [#9643](https://github.com/vuejs/vue/issues/9643)
-- **ssr:** not push non-async css files into map ([#9677](https://github.com/vuejs/vue/issues/9677)) ([d282400](https://github.com/vuejs/vue/commit/d28240009c4c49fb2ef42a79206f0d9ad03f736c))
-- **transition:** fix appear check for transition wrapper components ([#9668](https://github.com/vuejs/vue/issues/9668)) ([4de4649](https://github.com/vuejs/vue/commit/4de4649d9637262a9b007720b59f80ac72a5620c))
-- v-bind object should be overridable by single bindings ([#9653](https://github.com/vuejs/vue/issues/9653)) ([0b57380](https://github.com/vuejs/vue/commit/0b57380f10986c6b07e3c240acc06bfd2eddfd1b)), closes [#9641](https://github.com/vuejs/vue/issues/9641)
-
-## [2.6.8](https://github.com/vuejs/vue/compare/v2.6.7...v2.6.8) (2019-03-01)
-
-### Bug Fixes
-
-- avoid compression of unicode sequences by using regexps ([#9595](https://github.com/vuejs/vue/issues/9595)) ([7912f75](https://github.com/vuejs/vue/commit/7912f75c5eb09e0aef3e4bfd8a3bb78cad7540d7)), closes [#9456](https://github.com/vuejs/vue/issues/9456)
-- **compiler:** set end location for incomplete elements ([#9598](https://github.com/vuejs/vue/issues/9598)) ([cbad54a](https://github.com/vuejs/vue/commit/cbad54aa52847cfc934bb925d53c53ee57fc153d))
-- fix modifier parsing for dynamic argument with deep path ([#9585](https://github.com/vuejs/vue/issues/9585)) ([060c3b9](https://github.com/vuejs/vue/commit/060c3b98efa44a9f21bcc038a2593b1cc3c782e9)), closes [#9577](https://github.com/vuejs/vue/issues/9577)
-- further adjust max stack size ([571a488](https://github.com/vuejs/vue/commit/571a4880fc06b491a280325b79fd4cbb59ceb47e)), closes [#9562](https://github.com/vuejs/vue/issues/9562)
-- handle async component when parent is toggled before resolve ([#9572](https://github.com/vuejs/vue/issues/9572)) ([ed34113](https://github.com/vuejs/vue/commit/ed341137b23315b76ba391db1b0e537950c091e1)), closes [#9571](https://github.com/vuejs/vue/issues/9571)
-- scoped slots dynamic check should include v-for on element itself ([2277b23](https://github.com/vuejs/vue/commit/2277b2322cf81b5830a5b85f6600e1896edc7aa9)), closes [#9596](https://github.com/vuejs/vue/issues/9596)
-- **types:** allow scoped slots to return a single VNode ([#9563](https://github.com/vuejs/vue/issues/9563)) ([241eea1](https://github.com/vuejs/vue/commit/241eea19a64550bfdb3f9d7e4197127997572842))
-- **types:** update this for nextTick api ([#9541](https://github.com/vuejs/vue/issues/9541)) ([f333016](https://github.com/vuejs/vue/commit/f33301619d18b9392597c5230af17921c0b42466))
-
-## [2.6.7](https://github.com/vuejs/vue/compare/v2.6.6...v2.6.7) (2019-02-21)
-
-### Bug Fixes
-
-- **#9511:** avoid promise catch multiple times ([#9526](https://github.com/vuejs/vue/issues/9526)) ([2f3020e](https://github.com/vuejs/vue/commit/2f3020e9cc1ad5c878606b56bb73a30b1d9bb7d9)), closes [#9511](https://github.com/vuejs/vue/issues/9511) [#9511](https://github.com/vuejs/vue/issues/9511) [#9511](https://github.com/vuejs/vue/issues/9511) [#9511](https://github.com/vuejs/vue/issues/9511)
-- avoid errors thrown during dom props update ([8a80a23](https://github.com/vuejs/vue/commit/8a80a23ecba23f92f278d664388050ffcd121385)), closes [#9459](https://github.com/vuejs/vue/issues/9459)
-- avoid possible infinite loop by accessing observables in error handler ([#9489](https://github.com/vuejs/vue/issues/9489)) ([ee29e41](https://github.com/vuejs/vue/commit/ee29e41ef469b3ca3c793f04289075e3b128447f))
-- **compiler:** handle negative length in codeframe repeat ([7a8de91](https://github.com/vuejs/vue/commit/7a8de91cd78f523fabe8452652513250871a01c6))
-- ensure generated scoped slot code is compatible with 2.5 ([7ec4627](https://github.com/vuejs/vue/commit/7ec4627902020cccd7b3f4fbc63e1b0d6b9798cd)), closes [#9545](https://github.com/vuejs/vue/issues/9545)
-- ensure scoped slots update in conditional branches ([d9b27a9](https://github.com/vuejs/vue/commit/d9b27a92bd5277ee23a4e68a8bd31ecc72f4c99b)), closes [#9534](https://github.com/vuejs/vue/issues/9534)
-- scoped slots should update when inside v-for ([8f004ea](https://github.com/vuejs/vue/commit/8f004ea44e06d7764fa884212fa95c2033515928)), closes [#9506](https://github.com/vuejs/vue/issues/9506)
-
-## [2.6.6](https://github.com/vuejs/vue/compare/v2.6.5...v2.6.6) (2019-02-12)
-
-### Bug Fixes
-
-- ensure scoped slot containing passed down slot content updates properly ([21fca2f](https://github.com/vuejs/vue/commit/21fca2fffc3a75235a6656eb85ae40835e04bf69))
-- fix keyCode check for Chrome autofill fake key events ([29c348f](https://github.com/vuejs/vue/commit/29c348f3cf60c50a52cc98123f8c54fa8f5672fc)), closes [#9441](https://github.com/vuejs/vue/issues/9441)
-
-## [2.6.5](https://github.com/vuejs/vue/compare/v2.6.4...v2.6.5) (2019-02-11)
-
-### Bug Fixes
-
-- allow passing multiple arguments to scoped slot ([e7d49cd](https://github.com/vuejs/vue/commit/e7d49cdcf2fd9a612e0dac7a7bea318824210881)), closes [#9468](https://github.com/vuejs/vue/issues/9468)
-- bail out of event blocking for iOS bug ([0bad7e2](https://github.com/vuejs/vue/commit/0bad7e2a3508b55abaa8aec2a1bd9c1127305cb4)), closes [#9462](https://github.com/vuejs/vue/issues/9462)
-- do not cache scoped slots when mixed with normal slots ([060686d](https://github.com/vuejs/vue/commit/060686d6ea4d013129b4d2e93d7d2e5c93e09686))
-
-## [2.6.4](https://github.com/vuejs/vue/compare/v2.6.3...v2.6.4) (2019-02-08)
-
-### Bug Fixes
-
-- avoid breaking avoriaz edge case ([9011b83](https://github.com/vuejs/vue/commit/9011b83db79cf2f3563f8fccb2e41b5b863c3ee9))
-- avoid logging same error twice when thrown by user in global handler ([ca57920](https://github.com/vuejs/vue/commit/ca57920edb56000bfc87bb64f4e5e3450c03e13a)), closes [#9445](https://github.com/vuejs/vue/issues/9445)
-- empty scoped slot should return undefined ([57bc80a](https://github.com/vuejs/vue/commit/57bc80a546acb2bd092edd393228324b453ae4e2)), closes [#9452](https://github.com/vuejs/vue/issues/9452)
-- expose v-slot slots without scope on this.$slots ([0e8560d](https://github.com/vuejs/vue/commit/0e8560d0fc1c0fbf3a52464939701e0e44543b00)), closes [#9421](https://github.com/vuejs/vue/issues/9421) [#9458](https://github.com/vuejs/vue/issues/9458)
-- new syntax slots without scope should also be exposed on functional slots() ([8a80086](https://github.com/vuejs/vue/commit/8a800867fe61e5aa642e1e3da91bb890d07312f7))
-
-### Performance Improvements
-
-- cache result from functional ctx.slots() calls ([7a0dfd0](https://github.com/vuejs/vue/commit/7a0dfd0badf3054c95ac1ec66cc6e213f1592c95))
-- skip scoped slots normalization when possible ([099f3ba](https://github.com/vuejs/vue/commit/099f3ba60085a089ff369442bdb835f3868e47c0))
-
-## [2.6.3](https://github.com/vuejs/vue/compare/v2.6.2...v2.6.3) (2019-02-06)
-
-### Bug Fixes
-
-- async component should use render owner as force update context ([b9de23b](https://github.com/vuejs/vue/commit/b9de23b1008b52deca7e7df40843e318a42f3f53)), closes [#9432](https://github.com/vuejs/vue/issues/9432)
-- avoid exposing internal flags on $scopedSlots ([24b4640](https://github.com/vuejs/vue/commit/24b4640c1f268722f5ab8f03e68e2df897cfbdf6)), closes [#9443](https://github.com/vuejs/vue/issues/9443)
-- bail out scoped slot optimization when there are nested scopes ([4d4d22a](https://github.com/vuejs/vue/commit/4d4d22a3f6017c46d08b67afe46af43027b06629)), closes [#9438](https://github.com/vuejs/vue/issues/9438)
-- **compiler:** fix v-bind dynamic arguments on slot outlets ([96a09aa](https://github.com/vuejs/vue/commit/96a09aad99bdecbcc0e5c420077bf41893d4a745)), closes [#9444](https://github.com/vuejs/vue/issues/9444)
-- skip microtask fix if event is fired from different document ([dae7e41](https://github.com/vuejs/vue/commit/dae7e4182fbbb41e599953cc22e5d54dbb164070)), closes [#9448](https://github.com/vuejs/vue/issues/9448)
-- skip microtask fix in Firefix <= 53 ([7bc88f3](https://github.com/vuejs/vue/commit/7bc88f30c3eadded07dd5b460d1e7cb9342d017c)), closes [#9446](https://github.com/vuejs/vue/issues/9446)
-- **types:** add Vue.version to types ([#9431](https://github.com/vuejs/vue/issues/9431)) ([54e6a12](https://github.com/vuejs/vue/commit/54e6a121e992f20c03f104533caa4c59e59b1ee7))
-
-### Reverts
-
-- feat: expose all scoped slots on this.$slots ([d5ade28](https://github.com/vuejs/vue/commit/d5ade28652b07303ac6b713813792752ae5e4e04))
-
-## [2.6.2](https://github.com/vuejs/vue/compare/v2.6.1...v2.6.2) (2019-02-05)
-
-### Bug Fixes
-
-- always set transformed model value on attrs ([b034abf](https://github.com/vuejs/vue/commit/b034abf48e793189ce8796c259eed2fbfb79bcd0))
-- restore slot-scope + v-if behavior ([44a4ca3](https://github.com/vuejs/vue/commit/44a4ca33b95070e9aa53c6924479519d86dd9b36)), closes [#9422](https://github.com/vuejs/vue/issues/9422)
-
-### Features
-
-- expose all scoped slots on this.$slots ([0129b0e](https://github.com/vuejs/vue/commit/0129b0eb12a1f98a722f100892bfc5e60b0f51ce)), closes [#9421](https://github.com/vuejs/vue/issues/9421)
-
-## [2.6.1](https://github.com/vuejs/vue/compare/v2.6.0...v2.6.1) (2019-02-04)
-
-### Bug Fixes
-
-- avoid blocking first input event in IE when it shouldn't ([#9297](https://github.com/vuejs/vue/issues/9297)) ([0fb03b7](https://github.com/vuejs/vue/commit/0fb03b7831693b4abc90dd0bfe971c36c02d82a6)), closes [#7138](https://github.com/vuejs/vue/issues/7138) [#9042](https://github.com/vuejs/vue/issues/9042) [#9383](https://github.com/vuejs/vue/issues/9383)
-- avoid isPromise check when handler return value is Vue instance ([b6b42ca](https://github.com/vuejs/vue/commit/b6b42ca8c41963be292caa266ce4330603f4c4eb)), closes [#9418](https://github.com/vuejs/vue/issues/9418)
-- **compiler:** fix inline-template crashing ([#9365](https://github.com/vuejs/vue/issues/9365)) ([55bfb94](https://github.com/vuejs/vue/commit/55bfb94a33ecc9b33131ec0fb78bba2946e8fc75)), closes [#9361](https://github.com/vuejs/vue/issues/9361)
-- decode single quotes in html attributes ([#9341](https://github.com/vuejs/vue/issues/9341)) ([c27fe24](https://github.com/vuejs/vue/commit/c27fe24dc6088b517ab17c799a1852f97c22c076))
-- **template-compiler:** allow comments on the root node in templates ([#9408](https://github.com/vuejs/vue/issues/9408)) ([1922e7d](https://github.com/vuejs/vue/commit/1922e7d4d99d0397223b3919a1643aacb7afbbab)), closes [#9407](https://github.com/vuejs/vue/issues/9407)
-- **v-model:** add value to $attrs if not defined in props ([#9331](https://github.com/vuejs/vue/issues/9331)) ([66fd3c8](https://github.com/vuejs/vue/commit/66fd3c8dd1577d4b634731adf4be4d3db5bf1df6)), closes [#9330](https://github.com/vuejs/vue/issues/9330)
-
-# [2.6.0](https://github.com/vuejs/vue/compare/v2.6.0-beta.3...v2.6.0) (2019-02-04)
-
-### Bug Fixes
-
-- allow more enumerated values for contenteditable ([e632e9a](https://github.com/vuejs/vue/commit/e632e9a0759532e1d902871ca07b02c9ac267e7c)), closes [#9397](https://github.com/vuejs/vue/issues/9397)
-- fix child forceUpdate regression ([44a17ba](https://github.com/vuejs/vue/commit/44a17ba2cde7fb9a673486d44de7f29feb5c1882)), closes [#9396](https://github.com/vuejs/vue/issues/9396)
-- fix v-bind:style for camelCase properties with !important ([#9386](https://github.com/vuejs/vue/issues/9386)) ([539e481](https://github.com/vuejs/vue/commit/539e481f38706a6202f0eacf54c579362fbd5bb4))
-- template v-slot should work with v-else conditions ([2807fd2](https://github.com/vuejs/vue/commit/2807fd24b0c17179336f84d1725b69cd3c7a0aca))
-
-### Features
-
-- move v-bind.prop shorthand behind flag ([64f863b](https://github.com/vuejs/vue/commit/64f863bbb9c8147819ef4547dfdadba0239c6d59))
-
-# [2.6.0-beta.3](https://github.com/vuejs/vue/compare/v2.6.0-beta.2...v2.6.0-beta.3) (2019-01-30)
-
-### Features
-
-- detect and warn invalid dynamic argument expressions ([c9e3a5d](https://github.com/vuejs/vue/commit/c9e3a5d1d92500ac1e1d1eb6f3866a9df5eecf53))
-
-# [2.6.0-beta.2](https://github.com/vuejs/vue/compare/v2.6.0-beta.1...v2.6.0-beta.2) (2019-01-26)
-
-### Bug Fixes
-
-- async edge case fix should apply to more browsers ([ba0ebd4](https://github.com/vuejs/vue/commit/ba0ebd4771ddb5c56c1261f82c842b57ca7163a6))
-- fix checkbox event edge case in Firefox ([1868561](https://github.com/vuejs/vue/commit/1868561442507690f07579c258f4db19a650fb9a))
-
-### Features
-
-- adjust v-slot per RFC + enable flag ([67e85de](https://github.com/vuejs/vue/commit/67e85deae2b9720624ed7b20223b905a882942a0))
-- dynamic directive arguments for v-on, v-bind and custom directives ([#9373](https://github.com/vuejs/vue/issues/9373)) ([dbc0582](https://github.com/vuejs/vue/commit/dbc0582587f90e78867809bb6ae683301cd0626b))
-- **ssr:** allow template option to be function in renderToString ([#9324](https://github.com/vuejs/vue/issues/9324)) ([b65f6d7](https://github.com/vuejs/vue/commit/b65f6d78e0e480601b0042b1b5e8259343b629fb))
-- update new slot syntax per RFC revision ([4fca045](https://github.com/vuejs/vue/commit/4fca0454bd716a5d3ba32057ff2ed510af933c8d))
-- warning for ambiguous v-slot usage ([8d84572](https://github.com/vuejs/vue/commit/8d8457246daac543c5935aa3cbf62d362431da43))
-
-### Performance Improvements
-
-- improve scoped slots change detection accuracy ([#9371](https://github.com/vuejs/vue/issues/9371)) ([f219bed](https://github.com/vuejs/vue/commit/f219bedae8f9cab131eb5529769bcfdc91ce2912))
-
-# [2.6.0-beta.1](https://github.com/vuejs/vue/compare/v2.5.22...v2.6.0-beta.1) (2019-01-16)
-
-### Bug Fixes
-
-- allow \_ in watch paths (element compat) ([8b382b3](https://github.com/vuejs/vue/commit/8b382b3efb14986e9ea5213d0b57c244b70ae0e7))
-- always use microtasks for nextTick ([#8450](https://github.com/vuejs/vue/issues/8450)) ([850555d](https://github.com/vuejs/vue/commit/850555d1faa9be7d8306adffd95c7dee5e58717f)), closes [#7109](https://github.com/vuejs/vue/issues/7109) [#7546](https://github.com/vuejs/vue/issues/7546) [#7707](https://github.com/vuejs/vue/issues/7707) [#7834](https://github.com/vuejs/vue/issues/7834) [#8109](https://github.com/vuejs/vue/issues/8109) [#6566](https://github.com/vuejs/vue/issues/6566)
-- **core:** dedupe lifecycle hooks during options merge ([edf7df0](https://github.com/vuejs/vue/commit/edf7df0c837557dd3ea8d7b42ad8d4b21858ade0)), closes [#9199](https://github.com/vuejs/vue/issues/9199)
-- **core:** fix merged twice bug when passing extended constructor to mixins ([#9199](https://github.com/vuejs/vue/issues/9199)) ([5371617](https://github.com/vuejs/vue/commit/537161779ea329c1d0a993997555f1c692b8cac1)), closes [#9198](https://github.com/vuejs/vue/issues/9198)
-- cover more cases in v-on inline return value ([9432737](https://github.com/vuejs/vue/commit/9432737cf871335c42ce0dc0a0baa21a4d8c3832))
-- ensure only nromalize a scoped slot when it is present ([5fb23d4](https://github.com/vuejs/vue/commit/5fb23d4e2971480d14fbee0146e3416a07bc0b9f))
-- ensure proxied normal slot uses correct key ([b32c4b6](https://github.com/vuejs/vue/commit/b32c4b693535c35ae10742eb3351cbc123b15941))
-- make transition-group key warning a tip to avoid breaking compilation ([d08b49f](https://github.com/vuejs/vue/commit/d08b49f520e0704f9d4e61be4f751e3b2cdac6a8))
-- **next-tick:** revert 60da366 ([080dd97](https://github.com/vuejs/vue/commit/080dd971f77f7c631650c4e3027d1802f4e804d8)), closes [#8436](https://github.com/vuejs/vue/issues/8436)
-- **provide/inject:** Merges symbol provides ([#7926](https://github.com/vuejs/vue/issues/7926)) ([1933ee8](https://github.com/vuejs/vue/commit/1933ee80ff808b81a691fa6a135c1588d218bc0a))
-- return inline invocation return value in v-on handlers ([0ebb0f3](https://github.com/vuejs/vue/commit/0ebb0f39dfee0a5c03adb2f312f617cca37b44d6)), closes [#7628](https://github.com/vuejs/vue/issues/7628)
-- **runtime:** DevTools recommendation shows for all browsers ([#8638](https://github.com/vuejs/vue/issues/8638)) ([22ad266](https://github.com/vuejs/vue/commit/22ad26615104c15fd09bc69692e3042bb1bb58e9)), closes [#8634](https://github.com/vuejs/vue/issues/8634)
-- **scoped-slots:** ensure $scopedSlots calls always return Arrays ([c7c13c2](https://github.com/vuejs/vue/commit/c7c13c2a156269d29fd9c9f8f6a3e53a2f2cac3d)), closes [#8056](https://github.com/vuejs/vue/issues/8056)
-- **ssr:** properly handle invalid and numeric style properties ([7d9cfeb](https://github.com/vuejs/vue/commit/7d9cfebe39ffd531e0948668e688474b276cdec1)), closes [#9231](https://github.com/vuejs/vue/issues/9231)
-- **ssr:** should not render invalid numeric style values ([17d8bcb](https://github.com/vuejs/vue/commit/17d8bcb60edc14b2c23f5e6cc92f030897092e21))
-- **ssr:** should render 0 as valid value for style property with unit ([aef5b4e](https://github.com/vuejs/vue/commit/aef5b4e47811cb842315bd27d6919650da45279b))
-
-### Features
-
-- add browser ESM build ([861abf4](https://github.com/vuejs/vue/commit/861abf4bb940e89a6ae3c5c3e2dad4ed0bd53b3e))
-- add Vue.observable() for explicitly creating observable objects ([c50bbde](https://github.com/vuejs/vue/commit/c50bbde41c4a1868a8a0b33df3238346840bd37c))
-- **compiler/watch:** allow unicode characters in component names and watch paths ([#8666](https://github.com/vuejs/vue/issues/8666)) ([9c71852](https://github.com/vuejs/vue/commit/9c718522bac60d13d3b48d6b6512fccfd5cf8858)), closes [#8564](https://github.com/vuejs/vue/issues/8564)
-- **compiler:** add whitespace option, deprecate preserveWhitespace option ([e1abedb](https://github.com/vuejs/vue/commit/e1abedb9e66b21da8a7e93e175b9dabe334dfebd)), closes [#9208](https://github.com/vuejs/vue/issues/9208)
-- **compiler:** expose generateCodeFrame method ([a4ed58c](https://github.com/vuejs/vue/commit/a4ed58c076649a4536b40a9c98c974c77602c76b))
-- **compiler:** output codeframe in browser compiler ([325fc76](https://github.com/vuejs/vue/commit/325fc7693c1574e69fe542f1dbc334030a4b1ec3))
-- **compiler:** output source range for compiler errors ([#7127](https://github.com/vuejs/vue/issues/7127)) ([b31a1aa](https://github.com/vuejs/vue/commit/b31a1aa8870474b2ca782c45d55edac2932d4cc2)), closes [#6338](https://github.com/vuejs/vue/issues/6338)
-- **compiler:** support deindent: false in vue-template-compiler ([#7215](https://github.com/vuejs/vue/issues/7215)) ([bf0efb0](https://github.com/vuejs/vue/commit/bf0efb02b1f52cceb0bad8588cf6c90e22349049)), closes [#7054](https://github.com/vuejs/vue/issues/7054)
-- **config:** expose config.useEventDelegation and default to false ([3be1c5d](https://github.com/vuejs/vue/commit/3be1c5d67e8bdfc9387122317bf3779261010d45))
-- **core:** expose all slots on $scopedSlots as functions ([5d52262](https://github.com/vuejs/vue/commit/5d52262f1ce56d080c3438c4773a81dc5c8397aa))
-- **errors:** sync/async error handling for lifecycle hooks and v-on handlers ([#8395](https://github.com/vuejs/vue/issues/8395)) ([6e9fcfc](https://github.com/vuejs/vue/commit/6e9fcfc81d922a1b188268bf50d7e67c07d6d662)), closes [#6953](https://github.com/vuejs/vue/issues/6953) [#7653](https://github.com/vuejs/vue/issues/7653)
-- expose performance measures ([9ae80ac](https://github.com/vuejs/vue/commit/9ae80acde59d9d149ee5e4e2097f209eac6f834f)), closes [#7570](https://github.com/vuejs/vue/issues/7570)
-- **functional:** add scopedSlots to context in functional components ([#7941](https://github.com/vuejs/vue/issues/7941)) ([fb6aa06](https://github.com/vuejs/vue/commit/fb6aa0609045e69a0b6050bc7b6466b63be8d69d))
-- new scoped slot syntax implementation update per rfc ([c5c354d](https://github.com/vuejs/vue/commit/c5c354d593d6f6344e78ca098f1ae1fa3e83d6ed))
-- **ssr:** Add 'nonce' option to context for ssr outlet script ([#8047](https://github.com/vuejs/vue/issues/8047)) ([f036cce](https://github.com/vuejs/vue/commit/f036cce16377cec328bee03a3a4069275b320312)), closes [#7479](https://github.com/vuejs/vue/issues/7479)
-- **ssr:** add custom state serializer option ([4494012](https://github.com/vuejs/vue/commit/44940121eef4e2df5f3cb3c21f3f468af8b336bc)), closes [#6614](https://github.com/vuejs/vue/issues/6614)
-- **ssr:** allow opting-out of caching by returning false in serverCacheKey ([ab24285](https://github.com/vuejs/vue/commit/ab24285458c98e25d5749beb4edebef73672de4b)), closes [#8790](https://github.com/vuejs/vue/issues/8790)
-- **ssr:** ssrPrefetch option + context.rendered hook ([#9017](https://github.com/vuejs/vue/issues/9017)) ([d7a533d](https://github.com/vuejs/vue/commit/d7a533d6f85aae52aed03202fa5ccb774f0cb2ec))
-- support .property shorthand syntax for v-bind.prop modifier ([d2902ca](https://github.com/vuejs/vue/commit/d2902ca8ec5fd184fe81479fea1318553fdb8323)), closes [#7582](https://github.com/vuejs/vue/issues/7582)
-- support custom toString() in text interpolation and v-html ([#8217](https://github.com/vuejs/vue/issues/8217)) ([0e4e45e](https://github.com/vuejs/vue/commit/0e4e45ec741416e0042c29a53bbc0e58c8663f6e)), closes [#8093](https://github.com/vuejs/vue/issues/8093)
-- support slot-props and its shorthand ([584e89d](https://github.com/vuejs/vue/commit/584e89da4ab17e1ebdae0ae10be77ef9d230c7a0))
-- support v-html for SVG elements ([#8652](https://github.com/vuejs/vue/issues/8652)) ([a981c80](https://github.com/vuejs/vue/commit/a981c80d2aedb0d56f98865e39c981819fbf65d0))
-- **types:** add Prop to main type declaration file ([#6856](https://github.com/vuejs/vue/issues/6856)) ([5791072](https://github.com/vuejs/vue/commit/57910723c6ba68386c15e095e42c1ed9603c7bcf)), closes [#6850](https://github.com/vuejs/vue/issues/6850)
-- **types:** add types for vue-template-compiler ([#7918](https://github.com/vuejs/vue/issues/7918)) ([ced774b](https://github.com/vuejs/vue/commit/ced774be6ddbc53884d7a5d395514a9f62e32336))
-- use event delegation when possible ([b7f7f27](https://github.com/vuejs/vue/commit/b7f7f2756928f409950186c5d641034f362b392a)), closes [#6566](https://github.com/vuejs/vue/issues/6566)
-- v-bind.sync also listens for kebab-case update event ([#8297](https://github.com/vuejs/vue/issues/8297)) ([3fca527](https://github.com/vuejs/vue/commit/3fca52792ef83fa58a5c28882706d9e8a039790d)), closes [#6428](https://github.com/vuejs/vue/issues/6428)
-- **v-for:** support iterables in v-for ([#8179](https://github.com/vuejs/vue/issues/8179)) ([d40eb9c](https://github.com/vuejs/vue/commit/d40eb9c2880c8dd27fedb9fbc508823a15742274))
-
-## [2.5.22](https://github.com/vuejs/vue/compare/v2.5.21...v2.5.22) (2019-01-11)
-
-### Bug Fixes
-
-- **async component:** memory leak after synchronous async loading ([#9275](https://github.com/vuejs/vue/issues/9275)) ([d21e931](https://github.com/vuejs/vue/commit/d21e93139697be2c0a6fdc4ee74d30d2834a729f)), closes [#9229](https://github.com/vuejs/vue/issues/9229)
-- **core:** dedupe lifecycle hooks during options merge ([0d2e9c4](https://github.com/vuejs/vue/commit/0d2e9c46f16e9ec5bd0f3eebd2aa31c7f7493856)), closes [#9199](https://github.com/vuejs/vue/issues/9199)
-- **core:** fix merged twice bug when passing extended constructor to mixins ([#9199](https://github.com/vuejs/vue/issues/9199)) ([743edac](https://github.com/vuejs/vue/commit/743edacdb6fa0bb711e860b68373274f50c8baa5)), closes [#9198](https://github.com/vuejs/vue/issues/9198)
-- **ssr:** support rendering comment ([#9128](https://github.com/vuejs/vue/issues/9128)) ([b06c784](https://github.com/vuejs/vue/commit/b06c784b81a244e1bc2d028216fcd2ab873730b9))
-
-## [2.5.21](https://github.com/vuejs/vue/compare/v2.5.20...v2.5.21) (2018-12-11)
-
-### Bug Fixes
-
-- fix single v-for child optimization ([847e493](https://github.com/vuejs/vue/commit/847e493768371cec4718969e02bdb7f8463f4e03))
-- fix v-for component with undefined value ([4748760](https://github.com/vuejs/vue/commit/47487607fbb99339038cf84990ba341c25b5e20d)), closes [#9181](https://github.com/vuejs/vue/issues/9181)
-- **lifecycle:** beforeUpdated should not be called if component is destroyed ([#9171](https://github.com/vuejs/vue/issues/9171)) ([87bad80](https://github.com/vuejs/vue/commit/87bad80f0cb9a30b95d9410120ff6e3e2022a723)), closes [#8076](https://github.com/vuejs/vue/issues/8076)
-- **types:** accept primitive and falsy values in createElement children ([#9154](https://github.com/vuejs/vue/issues/9154)) ([d780dd2](https://github.com/vuejs/vue/commit/d780dd2e2adcf71f40c086055a659a9a2b4a8282)), closes [#8498](https://github.com/vuejs/vue/issues/8498)
-- **v-model:** properly handle multiline v-model expressions ([#9184](https://github.com/vuejs/vue/issues/9184)) ([3d44937](https://github.com/vuejs/vue/commit/3d449376d557c4533a9664f95df3a168ecee9bfa)), closes [#9183](https://github.com/vuejs/vue/issues/9183)
-- **weex:** support data class type that is string ([#9139](https://github.com/vuejs/vue/issues/9139)) ([d8285c5](https://github.com/vuejs/vue/commit/d8285c57a613c42eddf2d4f2b75c1cea6aa4703a)), closes [#9124](https://github.com/vuejs/vue/issues/9124)
-
-### Performance Improvements
-
-- skip normalization on single child element v-for ([4074104](https://github.com/vuejs/vue/commit/4074104fac219e61e542f4da3a4800975a8063f2))
-
-### Reverts
-
-- "chore: use keypress in TodoMVC example for IME input methods ([#9172](https://github.com/vuejs/vue/issues/9172))" ([80fb6b8](https://github.com/vuejs/vue/commit/80fb6b8da144bd9c2313702e23a35d72fa620135))
-
-## [2.5.20](https://github.com/vuejs/vue/compare/v2.5.19...v2.5.20) (2018-12-10)
-
-### Bug Fixes
-
-- **types:** avoid `this` in VueConstructor signature ([#9173](https://github.com/vuejs/vue/issues/9173)) ([e06d2af](https://github.com/vuejs/vue/commit/e06d2af276fc8d626a3b048f4d138a243aa690a4)), closes [/github.com/vuejs/vue-class-component/issues/294#issuecomment-445526936](https://github.com//github.com/vuejs/vue-class-component/issues/294/issues/issuecomment-445526936)
-
-## [2.5.19](https://github.com/vuejs/vue/compare/v2.5.18...v2.5.19) (2018-12-09)
-
-### Bug Fixes
-
-- **ssr:** should not warn for custom directives that do not have ssr implementation ([780dac5](https://github.com/vuejs/vue/commit/780dac561b9cd6c3cec28f154f76e7d28352ebf3)), closes [#9167](https://github.com/vuejs/vue/issues/9167)
-- **vdom:** remove unnecessary sameVnode condition ([0d4b35f](https://github.com/vuejs/vue/commit/0d4b35f55975946cb0eb4f7f5f35efe3d078473e)), closes [#9168](https://github.com/vuejs/vue/issues/9168)
-
-### Reverts
-
-- fix(sfc): avoid deindent when pad option is specified ([#7647](https://github.com/vuejs/vue/issues/7647)) ([5d721a4](https://github.com/vuejs/vue/commit/5d721a42b140865e50a78445fe21c5f270bde703))
-
-## [2.5.18](https://github.com/vuejs/vue/compare/v2.5.18-beta.0...v2.5.18) (2018-12-07)
-
-### Bug Fixes
-
-- **compiler:** fix codegen for v-for component inside template ([1b4a8a0](https://github.com/vuejs/vue/commit/1b4a8a0c1edaf9c7eb129ba61bca94ba607bbf56)), closes [#9142](https://github.com/vuejs/vue/issues/9142)
-- fix keyName checking for space and delete in IE11 ([#9150](https://github.com/vuejs/vue/issues/9150)) ([0ed0aad](https://github.com/vuejs/vue/commit/0ed0aad77228b95e9a61a87386736938837527f8)), closes [#9112](https://github.com/vuejs/vue/issues/9112)
-- **ssr:** fix ssr template publicPath generation ([f077ed1](https://github.com/vuejs/vue/commit/f077ed17af14bb2675db64b2aa2d023769219624)), closes [#9145](https://github.com/vuejs/vue/issues/9145)
-- **transition-group:** fix activeInstance regression ([8a2dbf5](https://github.com/vuejs/vue/commit/8a2dbf50105ea729125a42fecfe2c2f0371d7836)), closes [#9151](https://github.com/vuejs/vue/issues/9151)
-- **types:** correct scopedSlot types ([#9131](https://github.com/vuejs/vue/issues/9131)) ([448ba65](https://github.com/vuejs/vue/commit/448ba65d2b139b29f1e6891add9925ac22ffe10b)), closes [#8946](https://github.com/vuejs/vue/issues/8946)
-- **types:** type support for advanced async components ([#8438](https://github.com/vuejs/vue/issues/8438)) ([dfaf9e2](https://github.com/vuejs/vue/commit/dfaf9e24361e10ae68ce3951eaf48262cf90f0ec))
-
-## [2.5.18-beta.0](https://github.com/vuejs/vue/compare/v2.5.17-beta.0...v2.5.18-beta.0) (2018-12-02)
-
-### Bug Fixes
-
-- actually disable dep collection when invoking lifecycle hooks ([#9095](https://github.com/vuejs/vue/issues/9095)) ([0d62bb8](https://github.com/vuejs/vue/commit/0d62bb84ffa1af7a4826aecc11c429c7a020645c)), closes [#9046](https://github.com/vuejs/vue/issues/9046)
-- **compiler:** wrap scoped slots v-if conditions in parens ([#9119](https://github.com/vuejs/vue/issues/9119)) ([ef8524a](https://github.com/vuejs/vue/commit/ef8524ab7db8d64ac449ce74f5858aa9d91357ad)), closes [#9114](https://github.com/vuejs/vue/issues/9114)
-- **compiler:** maybeComponent should return true when "is" attribute exists ([#8114](https://github.com/vuejs/vue/issues/8114)) ([aef2a5f](https://github.com/vuejs/vue/commit/aef2a5f3dbd5e52ec9d5ce026d7b858539057186)), closes [#8101](https://github.com/vuejs/vue/issues/8101)
-- **compiler:** normalize potential functional component children in v-for ([#8558](https://github.com/vuejs/vue/issues/8558)) ([d483a49](https://github.com/vuejs/vue/commit/d483a49c86874b2e75863b661f81feecd46ae721)), closes [#8468](https://github.com/vuejs/vue/issues/8468)
-- **compiler:** should keep newline after unary tags in `<pre>`  ([#8965](https://github.com/vuejs/vue/issues/8965)) ([05001e6](https://github.com/vuejs/vue/commit/05001e695ebd0b0504d664197a4771463a0f5328)), closes [#8950](https://github.com/vuejs/vue/issues/8950)
-- **compiler:** templates inside v-pre should be rendered to HTML ([#8146](https://github.com/vuejs/vue/issues/8146)) ([ecac831](https://github.com/vuejs/vue/commit/ecac831691d27cf7a10ec73a004d3fbad7623d1a)), closes [#8041](https://github.com/vuejs/vue/issues/8041)
-- **component:** clean up memory leak after loading async component completes (fix [#8740](https://github.com/vuejs/vue/issues/8740)) ([#8755](https://github.com/vuejs/vue/issues/8755)) ([2e472c5](https://github.com/vuejs/vue/commit/2e472c5e5e559a7a4083b4164ffe0c3911ce0651))
-- **core:** avoid mutating original children when cloning vnode ([097f622](https://github.com/vuejs/vue/commit/097f6229dffc34af452b106ad2a3b58845588807)), closes [#7975](https://github.com/vuejs/vue/issues/7975)
-- **core:** properly handle reused vnodes ([530ca1b](https://github.com/vuejs/vue/commit/530ca1b2db315fbd0e360807b2031d26665c5d3d)), closes [#7913](https://github.com/vuejs/vue/issues/7913)
-- **core:** skip mixins and extends if child is already merged ([#8870](https://github.com/vuejs/vue/issues/8870)) ([80f17fa](https://github.com/vuejs/vue/commit/80f17fa498f5df0388412877799dbd7573c44b2d)), closes [#8865](https://github.com/vuejs/vue/issues/8865)
-- **data:** skip recursive call if values are identical ([#8967](https://github.com/vuejs/vue/issues/8967)) ([a7658e0](https://github.com/vuejs/vue/commit/a7658e03a16dc507f0abeba41aee705f773727d0))
-- **error handling:** handle errors on immediate watcher execution ([#8581](https://github.com/vuejs/vue/issues/8581)) ([2686818](https://github.com/vuejs/vue/commit/2686818beb5728e3b7aa22f47a3b3f0d39d90c8e)), closes [#8567](https://github.com/vuejs/vue/issues/8567)
-- fix potential xss vulnerability in ssr when using v-bind ([3d36a44](https://github.com/vuejs/vue/commit/3d36a443c755bf16f2656a8595dda9076f021a4a))
-- fix server env detection in wechat mini program ([#9075](https://github.com/vuejs/vue/issues/9075)) ([05e8bcf](https://github.com/vuejs/vue/commit/05e8bcfe5d308f280f3640df96bd170fbcf1a9b5))
-- **for:** use IE compatible regex in v-for regex ([#8048](https://github.com/vuejs/vue/issues/8048)) ([ecc239e](https://github.com/vuejs/vue/commit/ecc239e47516d7f9a93b2cd49da4a2000960b8f7)), closes [#7946](https://github.com/vuejs/vue/issues/7946)
-- handle undefined style properties in jsdom (fix [#7444](https://github.com/vuejs/vue/issues/7444)) ([#8281](https://github.com/vuejs/vue/issues/8281)) ([5cfdf1a](https://github.com/vuejs/vue/commit/5cfdf1a2484fa73b572eae4afd196dcf9e1912ba))
-- **lifecycle:** updated should not be called after component being destroyed ([#8381](https://github.com/vuejs/vue/issues/8381)) ([a64ff19](https://github.com/vuejs/vue/commit/a64ff1957c35270b818aa9cfdfb2acb6e42ce2a9)), closes [#8076](https://github.com/vuejs/vue/issues/8076)
-- make sure global state is restored in the case of an exception in macrotask callback ([#9093](https://github.com/vuejs/vue/issues/9093)) ([b111de4](https://github.com/vuejs/vue/commit/b111de486b1bdc747fe0f5795fe22697d151bb8c))
-- **parser:** allow CRLFs in string interpolations ([#8408](https://github.com/vuejs/vue/issues/8408)) ([8f04135](https://github.com/vuejs/vue/commit/8f04135dbaa5f5f0500d42c0968beba8043f5363)), closes [#8103](https://github.com/vuejs/vue/issues/8103)
-- replace hardcoded .parentNode with abstract ops, fix [#8713](https://github.com/vuejs/vue/issues/8713) ([#8714](https://github.com/vuejs/vue/issues/8714)) ([1e1ce0c](https://github.com/vuejs/vue/commit/1e1ce0cac7d6c22c980021cbd3cb207a47e85dfb))
-- **server:** use path.posix.join to generate public path ([#8177](https://github.com/vuejs/vue/issues/8177)) ([46b8d2c](https://github.com/vuejs/vue/commit/46b8d2c59dc259995a71662229ed52b8b8beeb38)), closes [#8167](https://github.com/vuejs/vue/issues/8167)
-- **sfc:** avoid deindent when pad option is specified ([#7647](https://github.com/vuejs/vue/issues/7647)) ([9d2f9a0](https://github.com/vuejs/vue/commit/9d2f9a034f9c40d5ba6d8b1e131b1bfb675dc1cf))
-- **shared:** check dates in looseEqual ([#7940](https://github.com/vuejs/vue/issues/7940)) ([db7287c](https://github.com/vuejs/vue/commit/db7287c23b11bdc032fb0786e6617f3c6c40c835)), closes [#7928](https://github.com/vuejs/vue/issues/7928)
-- **ssr:** adjust call stack size defer threshold ([e4b1b57](https://github.com/vuejs/vue/commit/e4b1b57fd7117a19cdb376dcb156606e0cc32a94)), closes [#8545](https://github.com/vuejs/vue/issues/8545)
-- **ssr:** check js assets more accurate in ssr webpack plugin ([#8639](https://github.com/vuejs/vue/issues/8639)) ([5624278](https://github.com/vuejs/vue/commit/5624278fbe5d85cfe578d749da12b1e73c3e61a9))
-- **ssr:** computed properties should pass vm as first argument in ssr ([#9090](https://github.com/vuejs/vue/issues/9090)) ([33e669b](https://github.com/vuejs/vue/commit/33e669b22f69a1f9c9147528360fe0bba85534f0)), closes [#8977](https://github.com/vuejs/vue/issues/8977)
-- **ssr:** fix double escaping of staticClass values ([#7859](https://github.com/vuejs/vue/issues/7859)) ([#8037](https://github.com/vuejs/vue/issues/8037)) ([c21b89e](https://github.com/vuejs/vue/commit/c21b89ebeda4c45024c2a71bc7a292d47ebc7ee1))
-- **ssr:** remove trailing hash in webpack module identifier when ([ae6dcd6](https://github.com/vuejs/vue/commit/ae6dcd63a017059644502f8741d8a514f3e9cf84))
-- **ssr:** render initial and used async css chunks ([#7902](https://github.com/vuejs/vue/issues/7902)) ([575b6e7](https://github.com/vuejs/vue/commit/575b6e77ab82b0bbc581aec3ea9b07135d2d1fcd)), closes [#7897](https://github.com/vuejs/vue/issues/7897)
-- **ssr:** resolve server directives the same as on client ([#9129](https://github.com/vuejs/vue/issues/9129)) ([3078352](https://github.com/vuejs/vue/commit/307835284a326569ea12c4a22c7dcb8f36d2d8ca)), closes [#8961](https://github.com/vuejs/vue/issues/8961)
-- support modifier combination of click.right + .once ([#8492](https://github.com/vuejs/vue/issues/8492)) ([eb60452](https://github.com/vuejs/vue/commit/eb604529c62e9954305889122f34499ad75b3b45))
-- **transition:** check existence of `el.parentNode` ([#8422](https://github.com/vuejs/vue/issues/8422)) ([0b16927](https://github.com/vuejs/vue/commit/0b16927c9d382b9cf134b131b898350340c2ee41)), closes [#8199](https://github.com/vuejs/vue/issues/8199)
-- **transition:** handle local-formatted floats in toMs function. ([#8495](https://github.com/vuejs/vue/issues/8495)) ([59d4351](https://github.com/vuejs/vue/commit/59d4351ad8fc042bc263a16ed45a56e9ff5b013e)), closes [#4894](https://github.com/vuejs/vue/issues/4894)
-- **transition:** transition-group should only listen for first-level children's end events ([#8374](https://github.com/vuejs/vue/issues/8374)) ([504d5da](https://github.com/vuejs/vue/commit/504d5da7eff1c77117c2f57b0c4238e56de80fc5))
-- **types:** accept `number` type as key on Vue.set/delete ([#8707](https://github.com/vuejs/vue/issues/8707)) ([#8709](https://github.com/vuejs/vue/issues/8709)) ([0ba79e2](https://github.com/vuejs/vue/commit/0ba79e2588309ba386f570ed84d372611c4dd165))
-- **types:** fix `renderError`arguments type ([#8636](https://github.com/vuejs/vue/issues/8636)) ([ac217d2](https://github.com/vuejs/vue/commit/ac217d2472bb92ce901ef1f46595b44a1b5d1a18)), closes [#8635](https://github.com/vuejs/vue/issues/8635)
-- **types:** fix vm.$once argument type ([#8995](https://github.com/vuejs/vue/issues/8995)) ([97086f3](https://github.com/vuejs/vue/commit/97086f365808a040f6cf1ddb12e2b3f63d7769bf)), closes [#8983](https://github.com/vuejs/vue/issues/8983)
-- **types:** make VNodeDirective properties optional, fix [#8013](https://github.com/vuejs/vue/issues/8013) ([#8003](https://github.com/vuejs/vue/issues/8003)) ([99a51b4](https://github.com/vuejs/vue/commit/99a51b452fa13fc4392e87215a8c3024adf5f710))
-- **types:** relax the return type of props default option ([#8537](https://github.com/vuejs/vue/issues/8537)) ([a9eb198](https://github.com/vuejs/vue/commit/a9eb198413e7b1baaf364e93ec3c093734529fe8))
-- **types:** support chain call for Vue.use and Vue.mixin ([#8595](https://github.com/vuejs/vue/issues/8595)) ([c711ec1](https://github.com/vuejs/vue/commit/c711ec189aaf46399756e34d933ba5e0b6576c36))
-- **types:** support typing $el as SVGElement ([#8809](https://github.com/vuejs/vue/issues/8809)) ([3cd4af4](https://github.com/vuejs/vue/commit/3cd4af4af0a8a67f5887d5fc967147d433c8612c))
-- v-bind object should be overridable with kebab-cased props ([#8845](https://github.com/vuejs/vue/issues/8845)) ([7585241](https://github.com/vuejs/vue/commit/758524134e71ae025238e16a4c1f2b30a1310fe8))
-- **v-model:** avoid duplicate model transforms ([7b7164c](https://github.com/vuejs/vue/commit/7b7164c11cbb74ed44ee086f0a82acfcc1ff47a2)), closes [#8436](https://github.com/vuejs/vue/issues/8436)
-- **v-on:** correctly remove once listener ([#8036](https://github.com/vuejs/vue/issues/8036)) ([19c33a7](https://github.com/vuejs/vue/commit/19c33a7e4072b03069f803263ed0c49feb5f73a9)), closes [#8032](https://github.com/vuejs/vue/issues/8032)
-- **v-pre:** skip compiling custom component tags in v-pre blocks (fix [#8286](https://github.com/vuejs/vue/issues/8286)) ([#8376](https://github.com/vuejs/vue/issues/8376)) ([a71853b](https://github.com/vuejs/vue/commit/a71853bfc5b6ee117af05408f4d75c80893d44e2))
-
-### Features
-
-- add async option ([#8240](https://github.com/vuejs/vue/issues/8240)) ([c944827](https://github.com/vuejs/vue/commit/c94482743c41e9bfc745aa06d63f7f83bdd56991))
-- **devtools:** store functional render context on vnode in development ([#8586](https://github.com/vuejs/vue/issues/8586)) ([4ecc21c](https://github.com/vuejs/vue/commit/4ecc21c29ec12bb33d3b426cb4d42c579e9b0f2d))
-- **server, webpack-plugin:** webpack 4 support ([#7839](https://github.com/vuejs/vue/issues/7839)) ([ef0b250](https://github.com/vuejs/vue/commit/ef0b25097957ae9ef9970be732d6e65cc78902e9))
-- **weex:** support object syntax of class ([#7930](https://github.com/vuejs/vue/issues/7930)) ([6226503](https://github.com/vuejs/vue/commit/62265035c0c400ad6ec213541dd7cca58dd71f6e))
-
-### Reverts
-
-- Revert "perf: avoid unnecessary re-renders when computed property value did not change (#7824)" ([6b1d431](https://github.com/vuejs/vue/commit/6b1d431a89f3f7438d01d8cc98546397f0983287)), closes [#7824](https://github.com/vuejs/vue/issues/7824)
-
-## [2.5.17-beta.0](https://github.com/vuejs/vue/compare/v2.5.16...v2.5.17-beta.0) (2018-03-23)
-
-### Bug Fixes
-
-- add missing `asyncMeta` during VNode cloning ([#7861](https://github.com/vuejs/vue/issues/7861)) ([8227fb3](https://github.com/vuejs/vue/commit/8227fb35240ab1f301c30a6ad5d4d25071fa7996))
-- beforeUpdate should be called before render and allow state mutation ([#7822](https://github.com/vuejs/vue/issues/7822)) ([b7445a2](https://github.com/vuejs/vue/commit/b7445a2b945dcded287601ace8e711ab5cf35ab5)), closes [#7481](https://github.com/vuejs/vue/issues/7481)
-- **codegen:** support IE11 and Edge use of "Esc" key ([#7887](https://github.com/vuejs/vue/issues/7887)) ([1bd6196](https://github.com/vuejs/vue/commit/1bd6196fb234c28754d9a27095afe0b5b84990ad)), closes [#7880](https://github.com/vuejs/vue/issues/7880)
-- correct the `has` implementation in the `_renderProxy` ([#7878](https://github.com/vuejs/vue/issues/7878)) ([7b38739](https://github.com/vuejs/vue/commit/7b387390aa917edffc0eabce0b4186ea1ef40e2c))
-- ensure init/prepatch hooks are still respected ([de42278](https://github.com/vuejs/vue/commit/de42278d34f6a800cec5c7eb781c1b8b83a829dd)), closes [vue-router#1338](https://github.com/vue-router/issues/1338)
-- invoke component node create hooks before insertion ([#7823](https://github.com/vuejs/vue/issues/7823)) ([f43ce3a](https://github.com/vuejs/vue/commit/f43ce3a5d8f73e273f2d03c9d86ea5662cda481a)), closes [#7531](https://github.com/vuejs/vue/issues/7531)
-- **observer:** invoke getters on initial observation if setter defined ([#7828](https://github.com/vuejs/vue/issues/7828)) ([7a145d8](https://github.com/vuejs/vue/commit/7a145d86430bad65271f4d6ab1344b215fefe52a))
-
-### Performance Improvements
-
-- avoid unnecessary re-renders when computed property value did not change ([#7824](https://github.com/vuejs/vue/issues/7824)) ([653aac2](https://github.com/vuejs/vue/commit/653aac2c57d15f0e93a2c1cc7e6fad156658df19)), closes [#7767](https://github.com/vuejs/vue/issues/7767)
-
-### Reverts
-
-- Revert "refactor: remove unnecessary checks (#7875)" ([903be9b](https://github.com/vuejs/vue/commit/903be9b91f7c41d40e228676df9d66d2c064fe23)), closes [#7875](https://github.com/vuejs/vue/issues/7875)
-
-## [2.5.16](https://github.com/vuejs/vue/compare/v2.5.15...v2.5.16) (2018-03-13)
-
-### Bug Fixes
-
-- allow multiline expression in v-for ([71b4b25](https://github.com/vuejs/vue/commit/71b4b25375fa4bcd929e1161c35cab133e4a7c23)), closes [#7792](https://github.com/vuejs/vue/issues/7792)
-- **core:** Make set/delete warning condition for undefined, null and ([#7818](https://github.com/vuejs/vue/issues/7818)) ([9084747](https://github.com/vuejs/vue/commit/9084747e307dc9b415ff8e2a788c6a585a2a8f6c)), closes [#7452](https://github.com/vuejs/vue/issues/7452)
-- fix keyName checking for arrow keys in IE11 ([4378fc5](https://github.com/vuejs/vue/commit/4378fc5124067c2b3a3517dd7f527edd9be2ad37)), closes [#7806](https://github.com/vuejs/vue/issues/7806)
-- fix regression on duplicate component init when using shared data objects ([984927a](https://github.com/vuejs/vue/commit/984927a1a98d10ad8af44f2accfb08d34d517610)), closes [#7805](https://github.com/vuejs/vue/issues/7805)
-- fix wrongly matched named slots in functional components ([62a922e](https://github.com/vuejs/vue/commit/62a922e865f5e578f67b386cb614abfc173d7851)), closes [#7817](https://github.com/vuejs/vue/issues/7817)
-- **keep-alive:** run prune after render for correct active component check ([215f877](https://github.com/vuejs/vue/commit/215f877d1b7eb6583f7adf15676ead8611f07379)), closes [#7566](https://github.com/vuejs/vue/issues/7566)
-- **model:** fix static input type being overwritten by v-bind object ([#7819](https://github.com/vuejs/vue/issues/7819)) ([a6169d1](https://github.com/vuejs/vue/commit/a6169d1eb71d64eacddf1738e72d21725e2bff00)), closes [#7811](https://github.com/vuejs/vue/issues/7811)
-- named slots for nested functional components ([6dd73e9](https://github.com/vuejs/vue/commit/6dd73e9ee44c09f04d3f616fcce18750a55e2e4f)), closes [#7710](https://github.com/vuejs/vue/issues/7710)
-- **ssr:** fix SSR for async functional components ([882e719](https://github.com/vuejs/vue/commit/882e7199fd8eee039291c4b9f7f324dcf46f32fd)), closes [#7784](https://github.com/vuejs/vue/issues/7784)
-- **ssr:** fix v-show inline style rendering when style binding is array ([#7814](https://github.com/vuejs/vue/issues/7814)) ([1a979c4](https://github.com/vuejs/vue/commit/1a979c44d6543d89f8a7e26ad7f995b1bf2aee3c)), closes [#7813](https://github.com/vuejs/vue/issues/7813)
-
-## [2.5.15](https://github.com/vuejs/vue/compare/v2.5.14...v2.5.15) (2018-03-10)
-
-### Bug Fixes
-
-- do not traverse VNodes when registering dependencies ([84a9a9d](https://github.com/vuejs/vue/commit/84a9a9d61057f6f40a9ad2bee456b39ef0a8f001)), closes [#7786](https://github.com/vuejs/vue/issues/7786)
-
-## [2.5.14](https://github.com/vuejs/vue/compare/v2.5.13...v2.5.14) (2018-03-09)
-
-### Bug Fixes
-
-- address potential regex backtrack ([cd33407](https://github.com/vuejs/vue/commit/cd334070f3b82d3f5892c4999cc290ccd4f56fd8))
-- allow codebase to be inlined directly in HTML ([#7314](https://github.com/vuejs/vue/issues/7314)) ([dccd182](https://github.com/vuejs/vue/commit/dccd182b6763d8ef1871949029c85495ca958246)), closes [#7298](https://github.com/vuejs/vue/issues/7298)
-- always install composition event listeners ([f7ca21e](https://github.com/vuejs/vue/commit/f7ca21eab1e0d661945aa6070fc988028c90966f)), closes [#7367](https://github.com/vuejs/vue/issues/7367)
-- clean up custom events when patched component no longer have events ([d8b0838](https://github.com/vuejs/vue/commit/d8b08387a293c99b95c1efcf2517447335a618db)), closes [#7294](https://github.com/vuejs/vue/issues/7294)
-- **codegen:** support filters with () in older browsers ([#7545](https://github.com/vuejs/vue/issues/7545)) ([dc97a39](https://github.com/vuejs/vue/commit/dc97a39c2f41ce57431d42d8b41811866f8e105c)), closes [#7544](https://github.com/vuejs/vue/issues/7544)
-- **core:** disable dependency collection in lifecycle hooks and data getter ([#7596](https://github.com/vuejs/vue/issues/7596)) ([318f29f](https://github.com/vuejs/vue/commit/318f29fcdf3372ff57a09be6d1dc595d14c92e70)), closes [#7573](https://github.com/vuejs/vue/issues/7573)
-- **core:** handle edge cases for functional component returning arrays ([8335217](https://github.com/vuejs/vue/commit/8335217cb4bd13fb07e08a76c07df0fceed6c197)), closes [#7282](https://github.com/vuejs/vue/issues/7282)
-- do not special case attributes for custom elements ([50b711a](https://github.com/vuejs/vue/commit/50b711af43708426e63b4ea529436b49fafc3f2e)), closes [#6864](https://github.com/vuejs/vue/issues/6864) [#6885](https://github.com/vuejs/vue/issues/6885)
-- fix config.productionTip ([ced00b1](https://github.com/vuejs/vue/commit/ced00b1dec8326a653cce225133927fe5b4a3109)), closes [#7565](https://github.com/vuejs/vue/issues/7565)
-- fix ssr env detection in weex ([#7375](https://github.com/vuejs/vue/issues/7375)) ([3eb37ac](https://github.com/vuejs/vue/commit/3eb37acf98e2d9737de897ebe7bdb7e9576bcc21))
-- **inject:** use hasOwn instead of 'in' for provideKey check ([#7460](https://github.com/vuejs/vue/issues/7460)) ([733c1be](https://github.com/vuejs/vue/commit/733c1be7f5983cdd9e8089a8088b235ba21a4dee)), closes [#7284](https://github.com/vuejs/vue/issues/7284)
-- install ssr helpers for functional context during SSR ([9b22d86](https://github.com/vuejs/vue/commit/9b22d86ab315a3c6061a6a4776eab1964304f92e)), closes [#7443](https://github.com/vuejs/vue/issues/7443) [nuxt/nuxt.js#2565](https://github.com/nuxt/nuxt.js/issues/2565)
-- **model:** fix array index binding for v-model checkbox ([#7671](https://github.com/vuejs/vue/issues/7671)) ([550c3c0](https://github.com/vuejs/vue/commit/550c3c0d14af5485bb7e507c504664a7136e9bf9)), closes [#7670](https://github.com/vuejs/vue/issues/7670)
-- **observer:** do not invoke getters on initial observation ([#7302](https://github.com/vuejs/vue/issues/7302)) ([7392dfc](https://github.com/vuejs/vue/commit/7392dfcc1d5fd7b257df5ae134f9eb2f0cc0a51e)), closes [#7280](https://github.com/vuejs/vue/issues/7280)
-- **ref:** allow ref key to be zero ([#7676](https://github.com/vuejs/vue/issues/7676)) ([e396eb3](https://github.com/vuejs/vue/commit/e396eb3445904f11232f2355f03e8356173d0e31)), closes [#7669](https://github.com/vuejs/vue/issues/7669)
-- respect type order when boolean casting multi-typed props ([81e1e47](https://github.com/vuejs/vue/commit/81e1e47cabbd479e2a285f03120944f1efffe749)), closes [#7485](https://github.com/vuejs/vue/issues/7485)
-- **show:** prevent transitions from starting on change truthy values ([#7524](https://github.com/vuejs/vue/issues/7524)) ([013d980](https://github.com/vuejs/vue/commit/013d98092868a0c6721831e91616c64f99119b74)), closes [#7523](https://github.com/vuejs/vue/issues/7523)
-- skip v-model & value binding collision check with dynamic type binding ([#7406](https://github.com/vuejs/vue/issues/7406)) ([1c0b4af](https://github.com/vuejs/vue/commit/1c0b4af5fd2f9e8173b8f4718018ee80a6313872)), closes [#7404](https://github.com/vuejs/vue/issues/7404)
-- support KeyboardEvent.key in built-in keyboard event modifiers ([#7121](https://github.com/vuejs/vue/issues/7121)) ([1c8e2e8](https://github.com/vuejs/vue/commit/1c8e2e88ed2d74a02178217b318564b73a096c18)), closes [#6900](https://github.com/vuejs/vue/issues/6900)
-- **transition:** should not add transition class when cancelled ([#7391](https://github.com/vuejs/vue/issues/7391)) ([5191f13](https://github.com/vuejs/vue/commit/5191f13472d1fc37bdd601079970201fde6bf13e)), closes [#7390](https://github.com/vuejs/vue/issues/7390)
-- **types:** add missing `listeners` type on RenderContext ([#7584](https://github.com/vuejs/vue/issues/7584)) ([db1b18c](https://github.com/vuejs/vue/commit/db1b18ceec51761f1bcd6160c51e02b36b86a9c2))
-- **types:** contravariant generic default in ComponentOption ([#7369](https://github.com/vuejs/vue/issues/7369)) ([6ee6849](https://github.com/vuejs/vue/commit/6ee684983b1f3384a4050d7c47cee7c6a325db8b))
-- **types:** fix wrong errorCaptured type ([#7712](https://github.com/vuejs/vue/issues/7712)) ([6b8516b](https://github.com/vuejs/vue/commit/6b8516b2dde52be643ee6855b45b253a17ed0461))
-- **types:** make render option in functional components to optional ([#7663](https://github.com/vuejs/vue/issues/7663)) ([b2092db](https://github.com/vuejs/vue/commit/b2092dbff9ab0ccfa8e59ed3ca540cca0715c683))
-- **types:** make VNodeChildrenArrayContents type more accurate ([#7287](https://github.com/vuejs/vue/issues/7287)) ([49aae6b](https://github.com/vuejs/vue/commit/49aae6bb157e0650507974b7a9a1b0f2215e400b))
-- **types:** prefer normal component over functional one ([#7687](https://github.com/vuejs/vue/issues/7687)) ([144bf5a](https://github.com/vuejs/vue/commit/144bf5a99e2ebd644f80bc8ab61cd1bf0366961a))
-- **v-model:** handle trailing whitespaces in expression ([#7737](https://github.com/vuejs/vue/issues/7737)) ([db58493](https://github.com/vuejs/vue/commit/db584931e20f9ad4b423cfc14d587f9d0240a565))
-- **v-on:** return handler value when using modifiers ([#7704](https://github.com/vuejs/vue/issues/7704)) ([6bc75ca](https://github.com/vuejs/vue/commit/6bc75cacb72c0cc7f3d1041b5d9ff447ac2f5f69))
-- **vdom:** svg inside foreignObject should be rendered with correct namespace (fix [#7330](https://github.com/vuejs/vue/issues/7330)) ([#7350](https://github.com/vuejs/vue/issues/7350)) ([0529961](https://github.com/vuejs/vue/commit/05299610ea3e89ddbcfe4d8ede0c298223766423))
-- **weex:** default value for editor, fix [#7165](https://github.com/vuejs/vue/issues/7165) ([#7286](https://github.com/vuejs/vue/issues/7286)) ([e055df8](https://github.com/vuejs/vue/commit/e055df82fec0e76e4bc65e5a265b42e208595430))
-
-### Features
-
-- support v-model dynamic type binding for v-bind="object" ([41838c8](https://github.com/vuejs/vue/commit/41838c8e8632ba78791996fbc697080b2764bb6a)), closes [#7296](https://github.com/vuejs/vue/issues/7296)
-- **weex:** adjust framework entry APIs and add flow annotations ([#7272](https://github.com/vuejs/vue/issues/7272)) ([472a289](https://github.com/vuejs/vue/commit/472a2896bd4f156be168edfecb6ac432b853beb4))
-- **weex:** support parse object literal in binding attrs and styles ([#7291](https://github.com/vuejs/vue/issues/7291)) ([ff8fcd2](https://github.com/vuejs/vue/commit/ff8fcd2e2b95694527018f7836bab781f8600d25))
-- **weex:** update new syntax for <recycle-list> ([7cc0b55](https://github.com/vuejs/vue/commit/7cc0b559e9e57fcb3baeae5d8d4c8964aa335b5e))
-- **weex:** update weex recycle-list compiler ([#7610](https://github.com/vuejs/vue/issues/7610)) ([d6200d7](https://github.com/vuejs/vue/commit/d6200d70261c4a8943190900e0721ede1c4a4f2b))
-
-## [2.5.13](https://github.com/vuejs/vue/compare/v2.5.12...v2.5.13) (2017-12-19)
-
-### Reverts
-
-- Revert "feat: auto cache inline prop literals to avoid child re-render" ([aac7634](https://github.com/vuejs/vue/commit/aac76349e70fe0971ee24a7a1f3dada0e3459fb8))
-
-## [2.5.12](https://github.com/vuejs/vue/compare/v2.5.11...v2.5.12) (2017-12-19)
-
-### Bug Fixes
-
-- **warning:** allow symbol as vdom key ([#7271](https://github.com/vuejs/vue/issues/7271)) ([bacb911](https://github.com/vuejs/vue/commit/bacb911f7df09ff4868b4c848a6d7778872dff5c))
-- **weex:** append as tree by default for recycle-list and cell-slot ([#7216](https://github.com/vuejs/vue/issues/7216)) ([d544d05](https://github.com/vuejs/vue/commit/d544d052a9c5ec113c253895211296120d58b6ab))
-- **weex:** update recycle-list v-for transform ([0ee81b2](https://github.com/vuejs/vue/commit/0ee81b24b5146bca315503e2f5aa3b01832735f1))
-
-### Features
-
-- **$compiler:** compile weex native directives in preTransformNode ([2d09ee3](https://github.com/vuejs/vue/commit/2d09ee3b8ce37e201d3973587d1cb442d5e08f31))
-- **$compiler:** supports compiling v-bind to the weex native directive in recycle-list ([8b893c1](https://github.com/vuejs/vue/commit/8b893c13d6ffa79f294fec76a228509ec48e4706))
-- **$compiler:** supports compiling v-else-if and v-else to the weex native directive ([2a1ce0d](https://github.com/vuejs/vue/commit/2a1ce0d92cb12c18f945f69ee5cb6914b389e35e))
-- **$compiler:** supports compiling v-for to the weex native directive ([9bd1483](https://github.com/vuejs/vue/commit/9bd1483803d046877bef4f7adf1d3a942085ea1b))
-- **$event:** support binding parameters on event handler within weex recycle-list ([acdc3c4](https://github.com/vuejs/vue/commit/acdc3c46e98919faa50b3e4cc308ab73b1a60bfe))
-- auto cache inline prop literals to avoid child re-render ([996eb00](https://github.com/vuejs/vue/commit/996eb00a0a0933376c9364c2d187e2bf0512ff0d))
-- **compile:** supports compiling v-if to the weex native directive ([7ad368e](https://github.com/vuejs/vue/commit/7ad368ebb6987bd4044f9df184d73ce14ca680f2))
-- **types:** extract VueConfiguration type for easy expansion ([#7273](https://github.com/vuejs/vue/issues/7273)) ([#7274](https://github.com/vuejs/vue/issues/7274)) ([c0d516c](https://github.com/vuejs/vue/commit/c0d516c283aa1a1c238b6eb8b8e55f64770d27e8))
-- **weex:** generate "[@render](https://github.com/render)" function for weex recycle-list ([#6987](https://github.com/vuejs/vue/issues/6987)) ([0c11aa8](https://github.com/vuejs/vue/commit/0c11aa8addf5ad852e37da358ce2887af72e4193))
-- **weex:** partially support lifecycles of virtual component ([#7242](https://github.com/vuejs/vue/issues/7242)) ([661bfe5](https://github.com/vuejs/vue/commit/661bfe552e16d3a7036012021ae3746cfc02710e))
-- **weex:** pass stateless component test case ([452a65c](https://github.com/vuejs/vue/commit/452a65c98a9354bb529185638475b72d8ca19543))
-- **weex:** recycle-list support stateful child component ([70b97ac](https://github.com/vuejs/vue/commit/70b97ac2f43099a57ce2fb0a23dea0553ba95189))
-- **weex:** recycle-list support WIP ([5254ee3](https://github.com/vuejs/vue/commit/5254ee31c481ac16cf8f822b0b4df0f7815ffff3))
-- **weex:** split text into separate module ([c104cc5](https://github.com/vuejs/vue/commit/c104cc582d647f5e10b90563cb80907b9e30ec12))
-- **weex:** support compiling `v-on` in the weex native directive ([#6892](https://github.com/vuejs/vue/issues/6892)) ([2cb8ea3](https://github.com/vuejs/vue/commit/2cb8ea3fee741807a15bf8f3049ab062a7d9508c))
-- **weex:** update weex utils ([#7115](https://github.com/vuejs/vue/issues/7115)) ([3b32652](https://github.com/vuejs/vue/commit/3b32652aa68a06d881e3149bb21ac8711887e7f6))
-- **weex:** WIP adjust component transform stage ([62e47c9](https://github.com/vuejs/vue/commit/62e47c9eb4446da79d66ad2385c199f31b4348d8))
-- **weex:** WIP fix flow + handle errors in recycle-list template render ([5c2ce00](https://github.com/vuejs/vue/commit/5c2ce0017ff8929e70ce9f701b91d950fb351adb))
-- **weex:** WIP implement virtual component ([#7165](https://github.com/vuejs/vue/issues/7165)) ([b8d33ec](https://github.com/vuejs/vue/commit/b8d33ecd9ab8b7a46d8558b4e2caf506235cd165))
-- **weex:** WIP invoke recycle-list child component with backing instance ([801f793](https://github.com/vuejs/vue/commit/801f793625273b39fd3f25abbaa04508d6651563))
-- **weex:** WIP mark recycle list child component root ([88f3889](https://github.com/vuejs/vue/commit/88f3889f19678981944339be8d22c3ebcd11f822))
-- **wip:** recycle list template inline expand ([ac99957](https://github.com/vuejs/vue/commit/ac999573ea6e4be3a421cdff79d66a5274ec58eb))
-
-### Reverts
-
-- revert prop object validation ([01c0750](https://github.com/vuejs/vue/commit/01c07503bf6af902dde06fafa8a0008ee3e303aa)), closes [#7279](https://github.com/vuejs/vue/issues/7279)
-- **weex:** remove the "receiveTasks" api and support component hook ([#7053](https://github.com/vuejs/vue/issues/7053)) ([0bf0cbe](https://github.com/vuejs/vue/commit/0bf0cbef76a9d107ea0d4fbd8f54f640a2c5b221))
-
-## [2.5.11](https://github.com/vuejs/vue/compare/v2.5.10...v2.5.11) (2017-12-14)
-
-### Bug Fixes
-
-- avoid unnecessary lowercase coersion in component name validation ([3f0c628](https://github.com/vuejs/vue/commit/3f0c628e2c0fe6bfaecc521c96c6cc12ff24c7c4)), closes [#7237](https://github.com/vuejs/vue/issues/7237)
-
-### Features
-
-- warn misspelled keys on prop validation object ([#7198](https://github.com/vuejs/vue/issues/7198)) ([d02bb37](https://github.com/vuejs/vue/commit/d02bb37efb3c4ee14b8cf9db22d1ab3340ba2c0f))
-
-## [2.5.10](https://github.com/vuejs/vue/compare/v2.5.9...v2.5.10) (2017-12-12)
-
-### Bug Fixes
-
-- **core:** warn duplicate keys in all cases ([#7200](https://github.com/vuejs/vue/issues/7200)) ([023f171](https://github.com/vuejs/vue/commit/023f171f58f7f1b36f0b3e69fc6d330366bfdf43)), closes [#7199](https://github.com/vuejs/vue/issues/7199)
-- data() should be called with vm as first argument in mixins ([bd4819e](https://github.com/vuejs/vue/commit/bd4819e6cf1c8d70d25aba2636e01f40faf59443)), closes [#7191](https://github.com/vuejs/vue/issues/7191)
-- more consistent component naming warnings across the API ([644274c](https://github.com/vuejs/vue/commit/644274cbd34e14e74e8931fa979b22dc2db04895)), closes [#7212](https://github.com/vuejs/vue/issues/7212)
-- revert shared static tree cache to avoid memory leak ([5875c7c](https://github.com/vuejs/vue/commit/5875c7c4906873c31b2feb66bb3ab6a19af6f5d7)), closes [#7184](https://github.com/vuejs/vue/issues/7184)
-- should not update in-focus input value with lazy modifier ([60da366](https://github.com/vuejs/vue/commit/60da366a2653a3984d79331d02ebb2ecf7e73a9a)), closes [#7153](https://github.com/vuejs/vue/issues/7153)
-- **ssr:** fix double escaping of ssrNode attribute values ([#7224](https://github.com/vuejs/vue/issues/7224)) ([73a89bf](https://github.com/vuejs/vue/commit/73a89bf9e53c0f7f00f193e1b1bb195a71ab761f)), closes [#7223](https://github.com/vuejs/vue/issues/7223)
-- **ssr:** properly handle errors in async component ([8936b8d](https://github.com/vuejs/vue/commit/8936b8d9c147441555fcfd4ac748d817ba5ff60e)), closes [#6778](https://github.com/vuejs/vue/issues/6778)
-- **v-for:** support array and nested destructuring in v-for ([f5ce6b5](https://github.com/vuejs/vue/commit/f5ce6b50cffef2e0eb8895c462b2433d8f8a701f))
-- **weex:** send createFinish signal after root component mounted ([#7154](https://github.com/vuejs/vue/issues/7154)) ([0da8bce](https://github.com/vuejs/vue/commit/0da8bced77654beb14c39ff3b4543b2ef37d1aff))
-
-## [2.5.9](https://github.com/vuejs/vue/compare/v2.5.8...v2.5.9) (2017-11-27)
-
-### Bug Fixes
-
-- block unnecessary input event on textarea placeholder in IE ([0f7c443](https://github.com/vuejs/vue/commit/0f7c443dca800204bc2e00876365869ee79e2d7b)), closes [#7138](https://github.com/vuejs/vue/issues/7138)
-- ensure functionalContext is cloned during slot clones ([604e081](https://github.com/vuejs/vue/commit/604e081d0456ed136b24b5f759c608d153dfae93)), closes [#7106](https://github.com/vuejs/vue/issues/7106)
-- fix async component resolving in sibling mounted hook ([dd21eac](https://github.com/vuejs/vue/commit/dd21eacc33fee8f8e6151fe1dcb419644f8f98c2)), closes [#7107](https://github.com/vuejs/vue/issues/7107)
-- fix v-for iterator parsing destructuring + parens without index ([aa82625](https://github.com/vuejs/vue/commit/aa8262540ac0115d56b53863302b9f8e69f8a03d))
-- **keep-alive:** should not destroy active instance when pruning cache ([3932a45](https://github.com/vuejs/vue/commit/3932a451a1419a97ea0200c5cb8096afe9a3e7e7)), closes [#7105](https://github.com/vuejs/vue/issues/7105)
-- **types:** add missing ssr renderToString signature ([14e9908](https://github.com/vuejs/vue/commit/14e99086c02f4bcda55e639fb0baf2c664591448))
-- **types:** add Promise signature for bundleRenderer.renderToString ([#7098](https://github.com/vuejs/vue/issues/7098)) ([3554eb2](https://github.com/vuejs/vue/commit/3554eb27269e151a0ef3d8c4ad9b29ec6664c471))
-- **types:** bump ts version and fix typing bugs ([#7135](https://github.com/vuejs/vue/issues/7135)) ([a71e653](https://github.com/vuejs/vue/commit/a71e653108e4ba56a70107662f3ee30cead59c18))
-- **types:** improve and test bundleRenderer.renderToString Promise types ([fcc1229](https://github.com/vuejs/vue/commit/fcc122931b504655c8255645d57612bc74c0f594))
-- **types:** use object and string instead of Object and String ([#7126](https://github.com/vuejs/vue/issues/7126)) ([d2e1d49](https://github.com/vuejs/vue/commit/d2e1d49c41ac633ea9410e1062b8e3e01f9d6b6d))
-
-## [2.5.8](https://github.com/vuejs/vue/compare/v2.5.7...v2.5.8) (2017-11-21)
-
-### Bug Fixes
-
-- fix v-for alias deconstruct regression ([ebcef58](https://github.com/vuejs/vue/commit/ebcef58645af1582ca3c8a19ec26967946970301)), closes [#7096](https://github.com/vuejs/vue/issues/7096)
-
-## [2.5.7](https://github.com/vuejs/vue/compare/v2.5.6...v2.5.7) (2017-11-20)
-
-### Bug Fixes
-
-- allow traversing reactive objects which are sealed ([#7080](https://github.com/vuejs/vue/issues/7080)) ([4c22d1d](https://github.com/vuejs/vue/commit/4c22d1d17ffd3a9340c3b17443c7989d04ab14c5))
-- fix <keep-alive> include/exclude logic for anonymous components ([a23b913](https://github.com/vuejs/vue/commit/a23b913796a7d18e76185607f250655e18a390c8))
-- improve error detector v-for identifier check ([d891cd1](https://github.com/vuejs/vue/commit/d891cd1761df22e1e0b1953c6ed7947fdb79d915)), closes [#6971](https://github.com/vuejs/vue/issues/6971)
-- **ssr:** fix bundle renderer require path on windows ([#7085](https://github.com/vuejs/vue/issues/7085)) ([063acb7](https://github.com/vuejs/vue/commit/063acb79ebc02344ab277196d4aea0577b113926))
-
-### Features
-
-- feat: add warning for ambiguous combined usage of slot-scope and v-for ([c264335](https://github.com/vuejs/vue/commit/c264335fbd1b1d838e3c1085b7d6dcd1c752aa43)), closes [#6817](https://github.com/vuejs/vue/issues/6817)
-
-## [2.5.6](https://github.com/vuejs/vue/compare/v2.5.5...v2.5.6) (2017-11-18)
-
-### Bug Fixes
-
-- fix v-model :value warning on custom component ([59dea37](https://github.com/vuejs/vue/commit/59dea374037ec2e6b1f5570a30774f2de0a44adc)), closes [#7084](https://github.com/vuejs/vue/issues/7084)
-
-## [2.5.5](https://github.com/vuejs/vue/compare/v2.5.4...v2.5.5) (2017-11-17)
-
-### Bug Fixes
-
-- init \_staticTrees to avoid runtime reference warning ([f5cd29e](https://github.com/vuejs/vue/commit/f5cd29e1d8197613c4dfb4013b240784c3b64e43)), closes [#7075](https://github.com/vuejs/vue/issues/7075)
-- keep-alive should not cache anonymous components ([4d8226f](https://github.com/vuejs/vue/commit/4d8226fb2c84fa2e13a2d8a86dea8a9a5c6ea95f)), closes [#6938](https://github.com/vuejs/vue/issues/6938)
-- should warn unknown components inside <keep-alive> ([6d6b373](https://github.com/vuejs/vue/commit/6d6b3739e132723915bc2209663db1b825307865))
-
-### Features
-
-- warn if both v-model and v-bind:value used on same element ([#7056](https://github.com/vuejs/vue/issues/7056)) ([1e14603](https://github.com/vuejs/vue/commit/1e146037fa4280b502d0edf95936bc67e87fd339)), closes [#7048](https://github.com/vuejs/vue/issues/7048) [#7048](https://github.com/vuejs/vue/issues/7048) [#7048](https://github.com/vuejs/vue/issues/7048)
-
-## [2.5.4](https://github.com/vuejs/vue/compare/v2.5.3...v2.5.4) (2017-11-16)
-
-### Bug Fixes
-
-- clone slot nodes for render fn usage as well ([13196b2](https://github.com/vuejs/vue/commit/13196b25b8a0a84b3936982177195d2e04f13f79)), closes [#7041](https://github.com/vuejs/vue/issues/7041)
-- normlaize [@click](https://github.com/click).right and [@click](https://github.com/click).middle ([daed1e7](https://github.com/vuejs/vue/commit/daed1e73557d57df244ad8d46c9afff7208c9a2d)), closes [#7020](https://github.com/vuejs/vue/issues/7020)
-- should warn unknown components during hydration ([df82aeb](https://github.com/vuejs/vue/commit/df82aeb0bf7454ac99d403000a1ac993e8d8d4de)), closes [#6998](https://github.com/vuejs/vue/issues/6998)
-- **ssr:** ensure hydrated class & style bindings are reactive ([5db86b4](https://github.com/vuejs/vue/commit/5db86b4e94857fdde3ae6b71e24da637bc116baa)), closes [#7063](https://github.com/vuejs/vue/issues/7063)
-- **transition:** fix out-in transition getting stuck with v-if ([#7023](https://github.com/vuejs/vue/issues/7023)) ([45d7ba8](https://github.com/vuejs/vue/commit/45d7ba842917a075d6cb2563c78210e3b9210a58)), closes [#6687](https://github.com/vuejs/vue/issues/6687)
-- **types:** expose VueConstructor ([#7002](https://github.com/vuejs/vue/issues/7002)) ([267ada0](https://github.com/vuejs/vue/commit/267ada04e8dd66f5c159dd6ba1b9f88fbbe78676))
-- **weex:** donot rethrow the captured error on weex platform ([#7024](https://github.com/vuejs/vue/issues/7024)) ([c2b1cfe](https://github.com/vuejs/vue/commit/c2b1cfe9ccd08835f2d99f6ce60f67b4de55187f))
-
-### Features
-
-- **weex:** support batch update styles and attributes ([#7046](https://github.com/vuejs/vue/issues/7046)) ([7cf188e](https://github.com/vuejs/vue/commit/7cf188e134fe7bfc9e8a16b763fb85b18eff1eac))
-
-## [2.5.3](https://github.com/vuejs/vue/compare/v2.5.2...v2.5.3) (2017-11-03)
-
-### Bug Fixes
-
-- $set should respect properties on prototype chain ([83ed926](https://github.com/vuejs/vue/commit/83ed92608d81349e1cac2e481ed079e51a490b2b)), closes [#6845](https://github.com/vuejs/vue/issues/6845)
-- also clone component slot children during deepClone ([1cf02ef](https://github.com/vuejs/vue/commit/1cf02efda206185cb72bbaafb00037fa6269e3f3)), closes [#6891](https://github.com/vuejs/vue/issues/6891) [#6915](https://github.com/vuejs/vue/issues/6915)
-- clean up target variables to avoid memory leaks ([#6932](https://github.com/vuejs/vue/issues/6932)) ([c355319](https://github.com/vuejs/vue/commit/c3553196b8b15a71f982bd5e04c61be52e87c828)), closes [#6931](https://github.com/vuejs/vue/issues/6931)
-- **core:** static trees should be cached on options ([#6826](https://github.com/vuejs/vue/issues/6826)) ([#6837](https://github.com/vuejs/vue/issues/6837)) ([b6c384d](https://github.com/vuejs/vue/commit/b6c384dd78b56bd247e6a34d5aea0d3903f5b7fd))
-- **events:** properly $off array of events ([#6949](https://github.com/vuejs/vue/issues/6949)) ([c24f3e4](https://github.com/vuejs/vue/commit/c24f3e4208cd045832002ee9916559f6fe0dc2b5))
-- handle encoded tabs and newlines in attributes for Chrome a[href] and IE/Edge ([cfd73c2](https://github.com/vuejs/vue/commit/cfd73c2386623341fdbb3ac636c4baf84ea89c2c)), closes [#6828](https://github.com/vuejs/vue/issues/6828) [#6916](https://github.com/vuejs/vue/issues/6916)
-- **keep-alive:** higher priority for exclude than include ([#6905](https://github.com/vuejs/vue/issues/6905)) ([604230f](https://github.com/vuejs/vue/commit/604230fe953f864be5dc70bd7d34f64ae43e4f7e))
-- **model:** correctly set select v-model initial value on patch ([#6910](https://github.com/vuejs/vue/issues/6910)) ([58a39df](https://github.com/vuejs/vue/commit/58a39dfa0e8c4a51959e9a84369dad8fbca0e6ac))
-- properly mark slot rendered flag in production mode ([4fe1a95](https://github.com/vuejs/vue/commit/4fe1a95d2953ecf765e27677fa70ebadb176d4c3)), closes [#6997](https://github.com/vuejs/vue/issues/6997)
-- **slots:** properly handle nested named slot passing ([5a9da95](https://github.com/vuejs/vue/commit/5a9da95b8a865416f082952a48416ffc091e4078)), closes [#6996](https://github.com/vuejs/vue/issues/6996)
-- special case for static muted attribute in firefox ([f2e00f7](https://github.com/vuejs/vue/commit/f2e00f756fb540fb09ce3414289c652ce172d85c)), closes [#6887](https://github.com/vuejs/vue/issues/6887)
-- **ssr:** properly render `<select v-model>` initial state ([e1657fd](https://github.com/vuejs/vue/commit/e1657fd7ce49bff3c3ecad3c56ae527347505c34)), closes [#6986](https://github.com/vuejs/vue/issues/6986)
-- **ssr:** properly render textarea value ([79c0d7b](https://github.com/vuejs/vue/commit/79c0d7bcfbcd1ac492e7ceb77f5024d09efdc6b3)), closes [#6986](https://github.com/vuejs/vue/issues/6986)
-- **ssr:** should not optimize root if conditions ([4ad9a56](https://github.com/vuejs/vue/commit/4ad9a56b229b156e633f3d575cd0e99ba5e474d9)), closes [#6907](https://github.com/vuejs/vue/issues/6907)
-- **types:** improve typing for better completion ([#6886](https://github.com/vuejs/vue/issues/6886)) ([98ea0a3](https://github.com/vuejs/vue/commit/98ea0a3b48e37719f278c10a8ee5fb94d7d5db4e))
-- **typing:** relax $options type for TS2.6+ ([#6819](https://github.com/vuejs/vue/issues/6819)) ([9caed00](https://github.com/vuejs/vue/commit/9caed00d20f37c750e39db4ec86d278b453f0e5d))
-- **v-model:** v-if / v-else not working with :type + v-model ([#6955](https://github.com/vuejs/vue/issues/6955)) ([0c703e3](https://github.com/vuejs/vue/commit/0c703e34d1a2083d9f162fcf0885deefb803182e)), closes [#6918](https://github.com/vuejs/vue/issues/6918)
-- **weex:** stop trim css units in richtext component ([#6927](https://github.com/vuejs/vue/issues/6927)) ([8a784d8](https://github.com/vuejs/vue/commit/8a784d8d2333f0a05569f6c11c5a0fb0ab3a164e))
-
-## [2.5.2](https://github.com/vuejs/vue/compare/v2.5.1...v2.5.2) (2017-10-13)
-
-### Bug Fixes
-
-- further adjust nextTick strategy ([4e0c485](https://github.com/vuejs/vue/commit/4e0c48511d49f331fde31fc87b6ca428330f32d1)), closes [#6813](https://github.com/vuejs/vue/issues/6813)
-
-## [2.5.1](https://github.com/vuejs/vue/compare/v2.5.0...v2.5.1) (2017-10-13)
-
-### Bug Fixes
-
-- backwards compat with checkbox code generated in < 2.5 ([5665eaf](https://github.com/vuejs/vue/commit/5665eaf985a56cfd183ce8ce93c4d813edbd2cf8)), closes [#6803](https://github.com/vuejs/vue/issues/6803)
-- fix empty array edge case in normalizeChildren ([1f84dd1](https://github.com/vuejs/vue/commit/1f84dd1c2488d12ef144d4b548b0e80647f9403c)), closes [#6790](https://github.com/vuejs/vue/issues/6790)
-- **ssr:** add semicolon before self-removal script ([#6794](https://github.com/vuejs/vue/issues/6794)) ([5a15a8d](https://github.com/vuejs/vue/commit/5a15a8d2089bb833b892123c31a2ca04a511c4c8))
-- **transition-group:** work around rollup tree shaking ([#6796](https://github.com/vuejs/vue/issues/6796)) ([60b1af9](https://github.com/vuejs/vue/commit/60b1af9e02b93d9223d2ed1f23e0a618537a4c96)), closes [#6792](https://github.com/vuejs/vue/issues/6792)
-- **v-model:** allow arbitrary naems for type binding ([#6802](https://github.com/vuejs/vue/issues/6802)) ([15031b8](https://github.com/vuejs/vue/commit/15031b85427df5409f0bc4c10589cc6259f8a5b2)), closes [#6800](https://github.com/vuejs/vue/issues/6800)
-- v-on="object" listeners should fire after high-priority ones ([08a7fb5](https://github.com/vuejs/vue/commit/08a7fb539f9d3ab5b08a3c6cec9a6628929be3be)), closes [#6805](https://github.com/vuejs/vue/issues/6805)
-
-# [2.5.0](https://github.com/vuejs/vue/compare/v2.4.4...v2.5.0) (2017-10-13)
-
-### Bug Fixes
-
-- add slot v-bind warning ([#6736](https://github.com/vuejs/vue/issues/6736)) ([514b90b](https://github.com/vuejs/vue/commit/514b90b64770cba9f905d2dff59dfa0e064e580c)), closes [#6677](https://github.com/vuejs/vue/issues/6677)
-- allow an object's Symbols to be observed ([#6704](https://github.com/vuejs/vue/issues/6704)) ([4fd2ce8](https://github.com/vuejs/vue/commit/4fd2ce813cd0a59bd544defe07f44a5731e45f84))
-- **compiler:** warn when inline-template component has no children (fix [#6703](https://github.com/vuejs/vue/issues/6703)) ([#6715](https://github.com/vuejs/vue/issues/6715)) ([baabd6d](https://github.com/vuejs/vue/commit/baabd6d14016c730fe40a4202ae9b8f75e80041c))
-- **core:** avoid observing VNodes ([4459b87](https://github.com/vuejs/vue/commit/4459b87de902cf3ba496a104304ca80d1c9824c1)), closes [#6610](https://github.com/vuejs/vue/issues/6610)
-- ensure nextTick are passed to errorHandler ([#6730](https://github.com/vuejs/vue/issues/6730)) ([ae347a5](https://github.com/vuejs/vue/commit/ae347a52259b24507a9c747c80d78a6beaa36de0))
-- fallback to Promise in non-DOM environments ([6d1f4cb](https://github.com/vuejs/vue/commit/6d1f4cb89a156bf5f84942b1031354aa93916cb7))
-- fix scoped CSS for nested nodes in functional components ([4216588](https://github.com/vuejs/vue/commit/421658884f7ca786747abf9b89e00925fdfdfba8))
-- handle errors in errorHandler ([2b5c83a](https://github.com/vuejs/vue/commit/2b5c83af6d8b15510424af4877d58c261ea02e16)), closes [#6714](https://github.com/vuejs/vue/issues/6714)
-- properly handle v-if on `<template>` scoped slot ([68bdbf5](https://github.com/vuejs/vue/commit/68bdbf508b915872627676d6bf987bdac9e5fe97)), closes [#6725](https://github.com/vuejs/vue/issues/6725)
-- prevent memory leak due to circular reference in vnodes ([405d8e9](https://github.com/vuejs/vue/commit/405d8e9f4c3201db2ae0e397d9191d9b94edc219)), closes [#6759](https://github.com/vuejs/vue/issues/6759)
-- properly render value on `<progress>` in IE/Edge ([c64f9ae](https://github.com/vuejs/vue/commit/c64f9ae1649175ee8cac1c7ecf3283897c948202)), closes [#6666](https://github.com/vuejs/vue/issues/6666)
-- **ref:** preserve ref on components after removing root element ([#6718](https://github.com/vuejs/vue/issues/6718)) ([6ad44e1](https://github.com/vuejs/vue/commit/6ad44e13e990951ff152a0fd7042613c5a87f1c0)), closes [#6632](https://github.com/vuejs/vue/issues/6632) [#6641](https://github.com/vuejs/vue/issues/6641)
-- resolve async component default for native dynamic import ([2876ed8](https://github.com/vuejs/vue/commit/2876ed870c5368a1767fbeddf06e94b55ebd6234)), closes [#6751](https://github.com/vuejs/vue/issues/6751)
-- **ssr:** fix hydration mismatch with adjacent text node from slots ([b080a14](https://github.com/vuejs/vue/commit/b080a14138262f0f274d0888555a11bd7387d576)), closes [vuejs/vue-loader#974](https://github.com/vuejs/vue-loader/issues/974)
-- **ssr:** handle inline template compilation error ([dff85b2](https://github.com/vuejs/vue/commit/dff85b230abda63839ed6b80d56ccfc6068b9ae0)), closes [#6766](https://github.com/vuejs/vue/issues/6766)
-- use correct ns inside `<foreignObject>` as root node ([cf1ff5b](https://github.com/vuejs/vue/commit/cf1ff5b0dc3d15c1e16821cb5e4fc984c74f07c1)), closes [#6642](https://github.com/vuejs/vue/issues/6642)
-- use MessageChannel for nextTick ([6e41679](https://github.com/vuejs/vue/commit/6e41679a96582da3e0a60bdbf123c33ba0e86b31)), closes [#6566](https://github.com/vuejs/vue/issues/6566) [#6690](https://github.com/vuejs/vue/issues/6690)
-- warn slot-scope when used as a prop ([8295f71](https://github.com/vuejs/vue/commit/8295f716657ffe516f30e84f29ca94f4a0aefabf))
-- work around old Chrome bug ([0f2cb09](https://github.com/vuejs/vue/commit/0f2cb09444e8b2a5fa41aaf8c94e6f2f43e00c2f)), closes [#6601](https://github.com/vuejs/vue/issues/6601)
-
-### Features
-
-- add .exact event modifier ([#5977](https://github.com/vuejs/vue/issues/5977)) ([9734e87](https://github.com/vuejs/vue/commit/9734e878ec4efe59f40fc97d9ef86273ad58a430)), closes [#5976](https://github.com/vuejs/vue/issues/5976)
-- add catchError option ([b3cd9bc](https://github.com/vuejs/vue/commit/b3cd9bc3940eb1e01da7081450929557d9c1651e))
-- add in-browser build for vue-template-compiler ([a5e5b31](https://github.com/vuejs/vue/commit/a5e5b31455e0d64f834dd691b7488e0e105d32c3))
-- add max prop for <keep-alive> ([2cba6d4](https://github.com/vuejs/vue/commit/2cba6d4cb1db8273ee45cccb8e50ebd87191244e))
-- **core:** call data method with this value ([#6760](https://github.com/vuejs/vue/issues/6760)) ([3a5432a](https://github.com/vuejs/vue/commit/3a5432a9e3f470ebafcef905281b830537897037)), closes [#6739](https://github.com/vuejs/vue/issues/6739)
-- functional component support for compiled templates ([ea0d227](https://github.com/vuejs/vue/commit/ea0d227d2ddfa5fc5e1112acf9cd485b4eae62cb))
-- improve template expression error message ([e38d006](https://github.com/vuejs/vue/commit/e38d0067521eee85febedc5f3ed3c24b5454c3a9)), closes [#6771](https://github.com/vuejs/vue/issues/6771)
-- **inject:** support providing default values for injections ([#6322](https://github.com/vuejs/vue/issues/6322)) ([88423fc](https://github.com/vuejs/vue/commit/88423fc66a2a4917dcdb7631a4594f05446283b1))
-- make vue and basic server renderer compatible in pure js runtimes ([c5d0fa0](https://github.com/vuejs/vue/commit/c5d0fa0503631b53338e5255bc8640da4b2fd4cb))
-- rename catchError -> errorCaptured ([6dac3db](https://github.com/vuejs/vue/commit/6dac3dbe441302cebb945b675f78f8e7247e2a97))
-- rename inject alias from "name" to "from" ([6893499](https://github.com/vuejs/vue/commit/68934997444c0047c49e419761dfad7fbc043a5d))
-- scoped CSS support for functional components ([050bb33](https://github.com/vuejs/vue/commit/050bb33f9b02589357c037623ea8cbf8ff13555b))
-- **ssr:** add shouldPrefetch option ([7bc899c](https://github.com/vuejs/vue/commit/7bc899ce0ec10be3fbd4bd7e78b66dd357249c81)), closes [#5964](https://github.com/vuejs/vue/issues/5964)
-- **ssr:** auto-remove initial state script if prod ([#6763](https://github.com/vuejs/vue/issues/6763)) ([2d32b5d](https://github.com/vuejs/vue/commit/2d32b5d1b663fa331ec256b73e937af15eb6e3d5)), closes [#6761](https://github.com/vuejs/vue/issues/6761)
-- **ssr:** renderToString return Promise ([f881dd1](https://github.com/vuejs/vue/commit/f881dd175a6764f6f80077df20f950dba63ca447)), closes [#6160](https://github.com/vuejs/vue/issues/6160)
-- support denoting normal elements as scoped slot ([dae173d](https://github.com/vuejs/vue/commit/dae173d96d15f47de6ce6961354d5c05e4273005))
-- support RegExp in ignoredElements ([#6769](https://github.com/vuejs/vue/issues/6769)) ([795b908](https://github.com/vuejs/vue/commit/795b908095b29e76435479879c1ade7ef759ce7b))
-- **types:** further improve Vue type declarations for canonical usage ([#6391](https://github.com/vuejs/vue/issues/6391)) ([db138e2](https://github.com/vuejs/vue/commit/db138e2254d71f6b96e033acf66ba43ad269841a))
-- **v-model:** create non-existent properties as reactive ([e1da0d5](https://github.com/vuejs/vue/commit/e1da0d585c797860533d6cb10ea3d09c7fb711fc)), closes [#5932](https://github.com/vuejs/vue/issues/5932)
-- **v-model:** support dynamic input type binding ([f3fe012](https://github.com/vuejs/vue/commit/f3fe012d5499f607656b152ce5fcb506c641f9f4))
-- v-on automatic key inference ([4987eeb](https://github.com/vuejs/vue/commit/4987eeb3a734a16a4978d1061f73039002d351e6))
-
-### Reverts
-
-- fix(v-model): fix input listener with modifier blocking v-model update ([62405aa](https://github.com/vuejs/vue/commit/62405aa9035d5f547c0440263f16f21c1325f100))
-
-## [2.4.4](https://github.com/vuejs/vue/compare/v2.4.3...v2.4.4) (2017-09-14)
-
-### Bug Fixes
-
-- **ssr:** fix bundleRenderer Promise rejection regression ([0c9534f](https://github.com/vuejs/vue/commit/0c9534ff0069b5289ea9598bcb4f5e5ac346c979))
-- **ssr:** fix style injection regression ([a2f73f2](https://github.com/vuejs/vue/commit/a2f73f2c2e28771e6597334bd86f82851ce0955e)), closes [#6603](https://github.com/vuejs/vue/issues/6603) [#6353](https://github.com/vuejs/vue/issues/6353)
-
-## [2.4.3](https://github.com/vuejs/vue/compare/v2.4.2...v2.4.3) (2017-09-13)
-
-### Bug Fixes
-
-- $off should ignore undefined handler argument ([fa6a729](https://github.com/vuejs/vue/commit/fa6a7290e3b8cb62fb7f999389f476617b56503e)), closes [#6591](https://github.com/vuejs/vue/issues/6591)
-- computed properties should not be cached during SSR ([06741f3](https://github.com/vuejs/vue/commit/06741f326625e2db78d092e586923b97ba006906)), closes [vuejs/vuex#877](https://github.com/vuejs/vuex/issues/877)
-- deep clone slot vnodes on re-render ([0529040](https://github.com/vuejs/vue/commit/0529040c17b8632032a43d142aac88386f6b4a1f)), closes [#6372](https://github.com/vuejs/vue/issues/6372)
-- **directive:** should invoke unbind & inserted on inner component root element change ([538ad20](https://github.com/vuejs/vue/commit/538ad20d8a37fe7ee2463ff20ac9557af70e0d33)), closes [#6513](https://github.com/vuejs/vue/issues/6513)
-- do not use MutationObserver in IE11 ([844a540](https://github.com/vuejs/vue/commit/844a540c647dfa93dc714540953524830dd3475a)), closes [#6466](https://github.com/vuejs/vue/issues/6466)
-- ensure $attrs and $listeners are always objects ([#6441](https://github.com/vuejs/vue/issues/6441)) ([59dbd4a](https://github.com/vuejs/vue/commit/59dbd4a414394a3ce581f9fbd9554da9af9e4b1d)), closes [#6263](https://github.com/vuejs/vue/issues/6263)
-- ensure outer bindings on nested HOC are properly re-applied on inner root element change ([a744497](https://github.com/vuejs/vue/commit/a7444975343f7828004d90bfb0deeb98db0f46e7))
-- handle special case for allowfullscreen on `<embed>` ([d77b953](https://github.com/vuejs/vue/commit/d77b95317cedae299605fb692e2c7c67796b17cb)), closes [#6202](https://github.com/vuejs/vue/issues/6202)
-- inherit SVG ns on component root node ([#6511](https://github.com/vuejs/vue/issues/6511)) ([89f0d29](https://github.com/vuejs/vue/commit/89f0d29f2d541aa5a1ac9690258cd7c7ee576ef6)), closes [#6506](https://github.com/vuejs/vue/issues/6506)
-- **inject:** exclude not enumerable keys of inject object ([#6346](https://github.com/vuejs/vue/issues/6346)) ([3ee62fd](https://github.com/vuejs/vue/commit/3ee62fd59e20030dd63c08c2390e803d034928fe)), closes [#6574](https://github.com/vuejs/vue/issues/6574)
-- preserve slot attribute if not resolved by Vue ([684cd7d](https://github.com/vuejs/vue/commit/684cd7d21aa7cb9a40fb4a8542c4e08fb3801a86)), closes [#6553](https://github.com/vuejs/vue/issues/6553)
-- **provide:** provide should default to parentVal during merging ([#6473](https://github.com/vuejs/vue/issues/6473)) ([3c21675](https://github.com/vuejs/vue/commit/3c216755f6eb656c6d864265a8dc7b51b3ae971b)), closes [#6436](https://github.com/vuejs/vue/issues/6436)
-- set value as domProp for `<progress>` ([7116af4](https://github.com/vuejs/vue/commit/7116af4e07520040ed7328c39d0a456808bfe1e1)), closes [#6561](https://github.com/vuejs/vue/issues/6561)
-- **ssr:** address possible xss vector ([5091e2c](https://github.com/vuejs/vue/commit/5091e2c9847601e329ac36d17eae90bb5cb77a91))
-- **ssr:** better handle v-html hydration ([0f00f8f](https://github.com/vuejs/vue/commit/0f00f8fc2b83b964bb929b729a7c9e3675b52106)), closes [#6519](https://github.com/vuejs/vue/issues/6519)
-- **ssr:** expose context.styles when no lifecycle styles are injected ([1f52a2a](https://github.com/vuejs/vue/commit/1f52a2a9f433452c15715131ed74433a43d5cfb7)), closes [#6353](https://github.com/vuejs/vue/issues/6353)
-- **ssr:** fix cachedEscape memory issue ([02f8b80](https://github.com/vuejs/vue/commit/02f8b806768d70c589e646c384e592e93387b994)), closes [#6332](https://github.com/vuejs/vue/issues/6332)
-- **ssr:** handle v-text/v-html with non-string value ([09106f0](https://github.com/vuejs/vue/commit/09106f066a1ba71431e4f9f26246aaf619153e2e)), closes [#6572](https://github.com/vuejs/vue/issues/6572)
-- **ssr:** should also escape static text content ([172dbf9](https://github.com/vuejs/vue/commit/172dbf9faf4cb71dff72c77fdfe80fa1932d1ba3)), closes [#6345](https://github.com/vuejs/vue/issues/6345)
-- support prop type checking for primitive wrapper objects ([#6450](https://github.com/vuejs/vue/issues/6450)) ([679cd1f](https://github.com/vuejs/vue/commit/679cd1fef448989bf645313c391e4134ecd9f593)), closes [#6447](https://github.com/vuejs/vue/issues/6447)
-- **transition:** consider async placeholder as valid child to return ([#6369](https://github.com/vuejs/vue/issues/6369)) ([a43d667](https://github.com/vuejs/vue/commit/a43d66743be2bd62b2398090663e41eeaf0dc75f)), closes [#6256](https://github.com/vuejs/vue/issues/6256)
-- **types:** add `inject` option in functional component options type ([#6530](https://github.com/vuejs/vue/issues/6530)) ([1baa0a7](https://github.com/vuejs/vue/commit/1baa0a7884cfa147df7623a34ee277f7d39c7a21))
-- **types:** allow variadic plugin use ([#6363](https://github.com/vuejs/vue/issues/6363)) ([38d5218](https://github.com/vuejs/vue/commit/38d52182bf8915628314e2aea7d2cc41ec39a0d6)), closes [#6357](https://github.com/vuejs/vue/issues/6357)
-- **v-model:** Allow using array value with array v-model in checkboxes ([#6220](https://github.com/vuejs/vue/issues/6220)) ([d6e6f1d](https://github.com/vuejs/vue/commit/d6e6f1deb180a4f47e94496724623b9e6d8e08b3)), closes [#6219](https://github.com/vuejs/vue/issues/6219)
-- **v-model:** avoid unnecessary change event on select options change ([d4d553c](https://github.com/vuejs/vue/commit/d4d553ced75d8c73e75b85cec398be4b09f6f669)), closes [#6193](https://github.com/vuejs/vue/issues/6193) [#6194](https://github.com/vuejs/vue/issues/6194)
-- **v-model:** fix input listener with modifier blocking v-model update ([6f312d6](https://github.com/vuejs/vue/commit/6f312d636c3d6049dc9e60007f88ea871b8e8173)), closes [#6552](https://github.com/vuejs/vue/issues/6552)
-- **vdom:** avoid diff de-opt when both head/tail are different ([230c6ae](https://github.com/vuejs/vue/commit/230c6ae7822347b9b2a659503291e45fcc58fe41)), closes [#6502](https://github.com/vuejs/vue/issues/6502)
-- **vdom:** Don't replace input for text-like type change ([#6344](https://github.com/vuejs/vue/issues/6344)) ([f76d16e](https://github.com/vuejs/vue/commit/f76d16ed9507d4c2a90243ea3d77ccf00df29346)), closes [#6313](https://github.com/vuejs/vue/issues/6313)
-
-### Features
-
-- **weex richtext:** support events and add more test cases ([d627161](https://github.com/vuejs/vue/commit/d627161a91b77ca15e0e30c0313abb33d6c17cbc))
-- **weex richtext:** support to parse styles and classList ([b609642](https://github.com/vuejs/vue/commit/b60964256c876de2516977c776201ef56ab13fb7))
-- **weex richtext:** treat richtext as runtime components ([3e4d926](https://github.com/vuejs/vue/commit/3e4d926336dfdbb5cc4f9d0daed44eb84b53b0de))
-- **weex:** add basic support of richtext ([f1c96e7](https://github.com/vuejs/vue/commit/f1c96e72b2369f3f8cc0078adb732a25cc7bfbfe))
-- **weex:** remove **weex_require_module** api ([a8146c0](https://github.com/vuejs/vue/commit/a8146c0c1074cfd8214a62309c372b25035fd838))
-- **weex:** return instance in createInstance ([0dc27dc](https://github.com/vuejs/vue/commit/0dc27dcdec72c1c2e12fb49fb95dceca45e84115))
-- **weex:** support nested components in richtext ([0ea2bb4](https://github.com/vuejs/vue/commit/0ea2bb4fb4d9c4d846ae5852c871c472c17f4e34))
-- **weex:** wrap IFFE for appCode ([f975fac](https://github.com/vuejs/vue/commit/f975fac2a8657590dcc23ea8ccae791d125bc935))
-
-### Performance Improvements
-
-- **core:** prevent iteration of arrays that should not be observable ([#6467](https://github.com/vuejs/vue/issues/6467)) ([aa820cb](https://github.com/vuejs/vue/commit/aa820cba37b69772868c9cdb69235c424e23f529)), closes [#6284](https://github.com/vuejs/vue/issues/6284)
-- deep clone slot vnodes on re-render ([#6478](https://github.com/vuejs/vue/issues/6478)) ([5346361](https://github.com/vuejs/vue/commit/53463619e5d19d35dfad1a4245a8dc583681feb3))
-- optimize the performance of hyphenate method. ([#6274](https://github.com/vuejs/vue/issues/6274)) ([14ee9e7](https://github.com/vuejs/vue/commit/14ee9e74bf68024fcb53c305b1f15c6aab6e89d3))
-- **v-model:** tweak setSelected ([41d774d](https://github.com/vuejs/vue/commit/41d774d112946f986bf0b0e3f30fd962c01ceba2))
-
-## [2.4.2](https://github.com/vuejs/vue/compare/v2.4.1...v2.4.2) (2017-07-21)
-
-### Bug Fixes
-
-- checkbox v-model="array" ignore false-value ([#6180](https://github.com/vuejs/vue/issues/6180)) ([3d14e85](https://github.com/vuejs/vue/commit/3d14e855e422b656859d1b419af43b94320fcfce)), closes [#6178](https://github.com/vuejs/vue/issues/6178)
-- **compile:** properly generate comments with special character ([#6156](https://github.com/vuejs/vue/issues/6156)) ([d03fa26](https://github.com/vuejs/vue/commit/d03fa26687605a43d9a0c3f395d1d32375f7eaee)), closes [#6150](https://github.com/vuejs/vue/issues/6150)
-- ensure looseEqual is not dependent on key enumeration order ([a8ac129](https://github.com/vuejs/vue/commit/a8ac129a5876a7eeae0137bf2f1b0968d4d6ffad)), closes [#5908](https://github.com/vuejs/vue/issues/5908)
-- include boolean in isPrimitive check ([#6127](https://github.com/vuejs/vue/issues/6127)) ([be3dc9c](https://github.com/vuejs/vue/commit/be3dc9c6e923248bcf81eb8240dd4f3c168fac59)), closes [#6126](https://github.com/vuejs/vue/issues/6126)
-- **parser:** only ignore the first newline in `<pre>` ([082fc39](https://github.com/vuejs/vue/commit/082fc3967db4d3290e901a38504dcd9bb698e561)), closes [#6146](https://github.com/vuejs/vue/issues/6146)
-- **provide/inject:** merge provide properly from mixins ([3036551](https://github.com/vuejs/vue/commit/303655116f8ec78f3b0ac99569637ad868dfe246)), closes [#6175](https://github.com/vuejs/vue/issues/6175)
-- **provide/inject:** resolve inject properly from mixins ([#6107](https://github.com/vuejs/vue/issues/6107)) ([b0f00e3](https://github.com/vuejs/vue/commit/b0f00e31e7d06edfdc733e2e7f24d5ca448759f9)), closes [#6093](https://github.com/vuejs/vue/issues/6093)
-- **transition:** should trigger transition hooks for v-show in ie9 ([9b4dbba](https://github.com/vuejs/vue/commit/9b4dbba384bc81a99abe429476729f80cb06d19a)), closes [#5525](https://github.com/vuejs/vue/issues/5525)
-- **v-bind:** respect .prop modifier on components ([#6159](https://github.com/vuejs/vue/issues/6159)) ([06b9b0b](https://github.com/vuejs/vue/commit/06b9b0bbadcc6c5afd300ed7748294e62ba00803))
-- **v-model:** use stricter check for `<select>` option update ([c70addf](https://github.com/vuejs/vue/commit/c70addf7d1a8e820ed80b6ab14aace5aa7b604c5)), closes [#6112](https://github.com/vuejs/vue/issues/6112)
-- **v-on:** revert component root data.on/data.nativeOn behavior for ([1713061](https://github.com/vuejs/vue/commit/17130611261fdbab70d0e5ab45036e4b612b17fe)), closes [#6109](https://github.com/vuejs/vue/issues/6109)
-- work around IE/Edge bug when accessing document.activeElement from iframe ([fc3d7cd](https://github.com/vuejs/vue/commit/fc3d7cd7a93534d76840418467f303d4b301fbcd)), closes [#6157](https://github.com/vuejs/vue/issues/6157)
-
-### Features
-
-- warn when assigning to computed property with no setter ([eb9168c](https://github.com/vuejs/vue/commit/eb9168cfc1816b53ddb1eccd6310173a37803897)), closes [#6078](https://github.com/vuejs/vue/issues/6078)
-
-### Reverts
-
-- perf: remove src directory from npm module ([#6072](https://github.com/vuejs/vue/issues/6072)) ([ec4b1be](https://github.com/vuejs/vue/commit/ec4b1be42a30a452cca53bbdfdc8404c7a53e890))
-
-## [2.4.1](https://github.com/vuejs/vue/compare/v2.4.0...v2.4.1) (2017-07-13)
-
-# [2.4.0](https://github.com/vuejs/vue/compare/v2.3.3...v2.4.0) (2017-07-13)
-
-### Bug Fixes
-
-- check enterToClass/leaveToClass existence before adding it ([#5912](https://github.com/vuejs/vue/issues/5912)) ([34d8c79](https://github.com/vuejs/vue/commit/34d8c796ac6a8e47bf23155bad71d07fafd1aa51)), closes [#5800](https://github.com/vuejs/vue/issues/5800)
-- **core:** add merge strategy for provide option ([#6025](https://github.com/vuejs/vue/issues/6025)) ([306997e](https://github.com/vuejs/vue/commit/306997eaf4ff36f4757c753c8a00ce3851e29cca)), closes [#6008](https://github.com/vuejs/vue/issues/6008)
-- **core:** should preserve reactivity-ness of injected objects ([8d66691](https://github.com/vuejs/vue/commit/8d66691ee264969447390822971b8caa029cac3e)), closes [#5913](https://github.com/vuejs/vue/issues/5913)
-- ensure cleanup in watcher.get ([#5988](https://github.com/vuejs/vue/issues/5988)) ([f6cd44c](https://github.com/vuejs/vue/commit/f6cd44c48b83640e5d3fbbea46d7b1b9cb439543)), closes [#5975](https://github.com/vuejs/vue/issues/5975)
-- handle arrays in v-on object syntax ([086e6d9](https://github.com/vuejs/vue/commit/086e6d98f4217542afcc2794717119c62dde89b8))
-- improve Vue.set/Vue.delete API to support multi type of array index ([#5973](https://github.com/vuejs/vue/issues/5973)) ([eea0920](https://github.com/vuejs/vue/commit/eea0920f14d0ea63d1b94c648eeb36ac7dfb4b05)), closes [#5884](https://github.com/vuejs/vue/issues/5884)
-- multiple merged vnode hooks not invoked properly ([91deb4f](https://github.com/vuejs/vue/commit/91deb4fd910afd51137820f120d4c26d0a99e629)), closes [#6076](https://github.com/vuejs/vue/issues/6076)
-- **parser:** the first newline following pre and textarea tag should be ignored ([#6022](https://github.com/vuejs/vue/issues/6022)) ([4d68079](https://github.com/vuejs/vue/commit/4d680794a5a345078827a3fee3db8658bd35ec3a))
-- prefetch should not have `as` attribute ([#5683](https://github.com/vuejs/vue/issues/5683)) ([ebca266](https://github.com/vuejs/vue/commit/ebca266d10febb5ab5ca0cfbcd0dfbff2f2c2170))
-- **ref:** refactor function registerRef ([#6039](https://github.com/vuejs/vue/issues/6039)) ([254d85c](https://github.com/vuejs/vue/commit/254d85cfc42d58bf9e3d0626ba959379bdc32d6f)), closes [#5997](https://github.com/vuejs/vue/issues/5997)
-- **ssr:** fix bundleRenderer mapped async chunks caching check ([#5963](https://github.com/vuejs/vue/issues/5963)) ([de42186](https://github.com/vuejs/vue/commit/de42186d52562a0ce506580484ff64fe86b765bd))
-- **ssr:** reference error when create $ssrContext for root component ([#5981](https://github.com/vuejs/vue/issues/5981)) ([5581654](https://github.com/vuejs/vue/commit/55816543c46e75aa53481ac95a89ff6f87a2d704)), closes [#5941](https://github.com/vuejs/vue/issues/5941)
-- support plugin with multi version vue ([#5985](https://github.com/vuejs/vue/issues/5985)) ([049f317](https://github.com/vuejs/vue/commit/049f3171a9d2e97f62c209a4b78a71ec9dae810f)), closes [#5970](https://github.com/vuejs/vue/issues/5970)
-- transition group should work with dynamic name ([#6006](https://github.com/vuejs/vue/issues/6006)) ([#6019](https://github.com/vuejs/vue/issues/6019)) ([d8d4ca6](https://github.com/vuejs/vue/commit/d8d4ca6763af55e1715bbc1e0fadd10e5be41db3))
-- v-bind object should not override props on scopedSlots ([#5995](https://github.com/vuejs/vue/issues/5995)) ([458030a](https://github.com/vuejs/vue/commit/458030ae19a51982d028dcacfc77ab2cfac8ac26))
-- **v-model:** fix input change check for type="number" ([0a9aab5](https://github.com/vuejs/vue/commit/0a9aab510bcca85c78ef06487b5dcf25d76d780d)), closes [#6069](https://github.com/vuejs/vue/issues/6069)
-- **v-model:** should generate component-specific code for tags with "is" attribute ([a1d1145](https://github.com/vuejs/vue/commit/a1d1145c9123f7175f3ac20b503cfa507ad455f4)), closes [#6066](https://github.com/vuejs/vue/issues/6066)
-- **v-model:** use consistent behavior during IME composition for other text-like input types (fix [#5902](https://github.com/vuejs/vue/issues/5902)) ([4acc8c8](https://github.com/vuejs/vue/commit/4acc8c8be1971112be45e0feb7fb7eddbfc9d247))
-
-### Features
-
-- add .editorconfig ([#5691](https://github.com/vuejs/vue/issues/5691)) ([0cc0b07](https://github.com/vuejs/vue/commit/0cc0b0726d389fca535b35e4593a5ecca3dde6c9))
-- add `comments` option to allow preserving comments in template ([#5951](https://github.com/vuejs/vue/issues/5951)) ([e4da249](https://github.com/vuejs/vue/commit/e4da249ab8ef32a0b8156c840c9d2b9773090f8a)), closes [#5392](https://github.com/vuejs/vue/issues/5392)
-- Add `defer` to body scripts ([#5704](https://github.com/vuejs/vue/issues/5704)) ([f3757eb](https://github.com/vuejs/vue/commit/f3757eb37bfe38cb2e8d87983a1f83d31ea9d30a))
-- **core:** $attrs, $listeners & inheritAttrs option ([6118759](https://github.com/vuejs/vue/commit/61187596b9af48f1cb7b1848ad3eccc02ac2509d)), closes [#5983](https://github.com/vuejs/vue/issues/5983)
-- **keep-alive:** support Array for include and exclude ([#5956](https://github.com/vuejs/vue/issues/5956)) ([51c595a](https://github.com/vuejs/vue/commit/51c595a7cef24e12094f66e0f8934fa41edde07d))
-- resolve ES module default when resolving async components ([0cd6ef3](https://github.com/vuejs/vue/commit/0cd6ef321b3168d6c46c7a870c3d2a53fd9d4bde))
-- **ssr:** inheritAttrs support in SSR ([6bf9772](https://github.com/vuejs/vue/commit/6bf97721f1e38713353d5ac9906c88dca2cdad9b))
-- support sync modifier for v-bind="object" ([#5943](https://github.com/vuejs/vue/issues/5943)) ([3965e50](https://github.com/vuejs/vue/commit/3965e5053a7d2f22e90f81d4a153d65c1c670897)), closes [#5937](https://github.com/vuejs/vue/issues/5937)
-- **types:** add declaration for inheritAttrs ([1f9e924](https://github.com/vuejs/vue/commit/1f9e924971d7894517075f7f0efeeb85994a7ba0))
-- **types:** expose $vnode ([1b7ddd7](https://github.com/vuejs/vue/commit/1b7ddd7a35fab8773508ed47f56d0716081cfa1a))
-- **v-on:** support v-on object syntax with no arguments ([11614d6](https://github.com/vuejs/vue/commit/11614d63b7862b68b11cc45c0891437c62a832d7))
-- **weex:** implement "weex.supports" api to support feature detection ([#6053](https://github.com/vuejs/vue/issues/6053)) ([b1512d8](https://github.com/vuejs/vue/commit/b1512d8b136e0a12aca8dde9e72bf5200d3afe09))
-
-### Performance Improvements
-
-- remove src directory from npm module ([#6072](https://github.com/vuejs/vue/issues/6072)) ([e761573](https://github.com/vuejs/vue/commit/e7615737f142e3350b53d09d3a46d7ec143d1ef4))
-
-## [2.3.3](https://github.com/vuejs/vue/compare/v2.3.2...v2.3.3) (2017-05-09)
-
-## [2.3.2](https://github.com/vuejs/vue/compare/v2.3.1...v2.3.2) (2017-05-02)
-
-## [2.3.1](https://github.com/vuejs/vue/compare/v2.3.0...v2.3.1) (2017-05-02)
-
-# [2.3.0](https://github.com/vuejs/vue/compare/v2.3.0-beta.1...v2.3.0) (2017-04-27)
-
-# [2.3.0-beta.1](https://github.com/vuejs/vue/compare/v2.2.6...v2.3.0-beta.1) (2017-04-26)
-
-## [2.2.6](https://github.com/vuejs/vue/compare/v2.2.5...v2.2.6) (2017-03-27)
-
-## [2.2.5](https://github.com/vuejs/vue/compare/v2.2.4...v2.2.5) (2017-03-24)
-
-### Bug Fixes
-
-- **inject:** change warn message when trying to mutate an injected value ([#5243](https://github.com/vuejs/vue/issues/5243)) ([23a058e](https://github.com/vuejs/vue/commit/23a058ed13e7faa667ada2b96e242eb7488b601c))
-
-## [2.2.4](https://github.com/vuejs/vue/compare/v2.2.3...v2.2.4) (2017-03-13)
-
-## [2.2.3](https://github.com/vuejs/vue/compare/v2.2.2...v2.2.3) (2017-03-13)
-
-## [2.2.2](https://github.com/vuejs/vue/compare/v2.2.1...v2.2.2) (2017-03-09)
-
-## [2.2.1](https://github.com/vuejs/vue/compare/v2.2.0...v2.2.1) (2017-02-26)
-
-# [2.2.0](https://github.com/vuejs/vue/compare/v2.2.0-beta.2...v2.2.0) (2017-02-26)
-
-# [2.2.0-beta.2](https://github.com/vuejs/vue/compare/v2.2.0-beta.1...v2.2.0-beta.2) (2017-02-25)
-
-### Reverts
-
-- Revert "[WIP] Support for ref callback (#4807)" ([e7a2510](https://github.com/vuejs/vue/commit/e7a2510e631bd25f46b4e1125b83a9236a50ff1d)), closes [#4807](https://github.com/vuejs/vue/issues/4807)
-
-# [2.2.0-beta.1](https://github.com/vuejs/vue/compare/v2.1.10...v2.2.0-beta.1) (2017-02-24)
-
-### Bug Fixes
-
-- **sfc:** component contains '<' only ([#4753](https://github.com/vuejs/vue/issues/4753)) ([938fa4e](https://github.com/vuejs/vue/commit/938fa4efcc9bf6232bf5ace5920398dc2e128ac9))
-
-### Features
-
-- allow customization of component v-model prop/event via model option (close [#4515](https://github.com/vuejs/vue/issues/4515)) ([9d6c8ec](https://github.com/vuejs/vue/commit/9d6c8ec268f659a715e3b38c97a1e03964961703))
-- config.performance ([689c107](https://github.com/vuejs/vue/commit/689c107de4624879a5b6282ce43eed5ea3907b38))
-- implement template option for vue-server-renderer ([1c79592](https://github.com/vuejs/vue/commit/1c79592524339773d6397b264b2b489606cd55cb))
-- provide/inject (close [#4029](https://github.com/vuejs/vue/issues/4029)) ([f916bcf](https://github.com/vuejs/vue/commit/f916bcf37105903290ad2353db9a9436536d6859))
-- renderError ([1861ee9](https://github.com/vuejs/vue/commit/1861ee9570730149e01f225323c3a52392e5900f))
-- support multi-chunk bundles in ssr bundle renderer ([561447d](https://github.com/vuejs/vue/commit/561447d278da26e95c488ea75856823557b66c5e))
-
-## [2.1.10](https://github.com/vuejs/vue/compare/v2.1.9...v2.1.10) (2017-01-17)
-
-## [2.1.9](https://github.com/vuejs/vue/compare/v2.1.8...v2.1.9) (2017-01-16)
-
-### Reverts
-
-- Revert "also bind static special attrs as props (fix #4530)" ([ab0a225](https://github.com/vuejs/vue/commit/ab0a2259e0ef3f4ebdc8d0d4b929fae3e3f579d8)), closes [#4530](https://github.com/vuejs/vue/issues/4530)
-- Revert "Mark node with static props as static (#4662)" ([4e830ba](https://github.com/vuejs/vue/commit/4e830ba3c3c92e78bf3ffbea82a583865deaa52a)), closes [#4662](https://github.com/vuejs/vue/issues/4662)
-
-## [2.1.8](https://github.com/vuejs/vue/compare/v2.1.7...v2.1.8) (2016-12-28)
-
-### Reverts
-
-- Revert "remove no longer necessary code" ([fcc98d5](https://github.com/vuejs/vue/commit/fcc98d52e21bb634c3f0c50eeb4ee87494a7196f))
-- Revert "ensure leave transitions and enter transitions are triggered in the same frame (fix #4510)" ([02e2d99](https://github.com/vuejs/vue/commit/02e2d99e277a1ba1bd42e1b81b2273903fdb7fbc)), closes [#4510](https://github.com/vuejs/vue/issues/4510)
-- Revert "fix enter transition flicker regression (fix #4576)" ([0bb2d4e](https://github.com/vuejs/vue/commit/0bb2d4e2b621950f5d44ed83b7e9ce15e282db68)), closes [#4576](https://github.com/vuejs/vue/issues/4576)
-
-## [2.1.7](https://github.com/vuejs/vue/compare/v2.1.6...v2.1.7) (2016-12-24)
-
-## [2.1.6](https://github.com/vuejs/vue/compare/v2.1.5...v2.1.6) (2016-12-13)
-
-## [2.1.5](https://github.com/vuejs/vue/compare/v2.1.4...v2.1.5) (2016-12-13)
-
-## [2.1.4](https://github.com/vuejs/vue/compare/v2.1.3...v2.1.4) (2016-12-02)
-
-## [2.1.3](https://github.com/vuejs/vue/compare/v2.1.2...v2.1.3) (2016-11-24)
-
-## [2.1.2](https://github.com/vuejs/vue/compare/v2.1.1...v2.1.2) (2016-11-23)
-
-## [2.1.1](https://github.com/vuejs/vue/compare/v2.1.0...v2.1.1) (2016-11-23)
-
-# [2.1.0](https://github.com/vuejs/vue/compare/v2.0.8...v2.1.0) (2016-11-22)
-
-## [2.0.8](https://github.com/vuejs/vue/compare/v2.0.7...v2.0.8) (2016-11-20)
-
-## [2.0.7](https://github.com/vuejs/vue/compare/v2.0.6...v2.0.7) (2016-11-16)
-
-## [2.0.6](https://github.com/vuejs/vue/compare/v2.0.5...v2.0.6) (2016-11-15)
-
-### Reverts
-
-- Revert "fix #4041, warn overriding Vue's internal methods (#4111)" ([1bcc571](https://github.com/vuejs/vue/commit/1bcc571739d7228db0bc947ee67c20dde5aeb7e0)), closes [#4041](https://github.com/vuejs/vue/issues/4041) [#4111](https://github.com/vuejs/vue/issues/4111)
-
-## [2.0.5](https://github.com/vuejs/vue/compare/v2.0.4...v2.0.5) (2016-11-05)
-
-## [2.0.4](https://github.com/vuejs/vue/compare/v2.0.3...v2.0.4) (2016-11-04)
-
-## [2.0.3](https://github.com/vuejs/vue/compare/v2.0.2...v2.0.3) (2016-10-13)
-
-## [2.0.2](https://github.com/vuejs/vue/compare/v2.0.1...v2.0.2) (2016-10-12)
-
-### Reverts
-
-- Revert "fix select multiple first option auto-selected in Chrome/FF (fix #3852)" ([d0cfd54](https://github.com/vuejs/vue/commit/d0cfd549ba24edc7dced17ef7b8f410c4ea197f0)), closes [#3852](https://github.com/vuejs/vue/issues/3852)
-
-## [2.0.1](https://github.com/vuejs/vue/compare/v2.0.0...v2.0.1) (2016-09-30)
-
-# [2.0.0](https://github.com/vuejs/vue/compare/v2.0.0-rc.8...v2.0.0) (2016-09-30)
-
-# [2.0.0-rc.8](https://github.com/vuejs/vue/compare/v2.0.0-rc.7...v2.0.0-rc.8) (2016-09-27)
-
-# [2.0.0-rc.7](https://github.com/vuejs/vue/compare/v2.0.0-rc.6...v2.0.0-rc.7) (2016-09-23)
-
-# [2.0.0-rc.6](https://github.com/vuejs/vue/compare/v2.0.0-rc.5...v2.0.0-rc.6) (2016-09-13)
-
-# [2.0.0-rc.5](https://github.com/vuejs/vue/compare/v2.0.0-rc.4...v2.0.0-rc.5) (2016-09-08)
-
-# [2.0.0-rc.4](https://github.com/vuejs/vue/compare/v2.0.0-rc.3...v2.0.0-rc.4) (2016-08-29)
-
-# [2.0.0-rc.3](https://github.com/vuejs/vue/compare/v2.0.0-rc.2...v2.0.0-rc.3) (2016-08-20)
-
-# [2.0.0-rc.2](https://github.com/vuejs/vue/compare/v2.0.0-rc.1...v2.0.0-rc.2) (2016-08-16)
-
-### Reverts
-
-- Revert "support transition on component with v-show in root node (fix #3431)" ([68be112](https://github.com/vuejs/vue/commit/68be112652060f9a7123b594a3b18ca5fc31b033)), closes [#3431](https://github.com/vuejs/vue/issues/3431)
-
-# [2.0.0-rc.1](https://github.com/vuejs/vue/compare/v2.0.0-beta.8...v2.0.0-rc.1) (2016-08-11)
-
-# [2.0.0-beta.8](https://github.com/vuejs/vue/compare/v2.0.0-beta.7...v2.0.0-beta.8) (2016-08-10)
-
-# [2.0.0-beta.7](https://github.com/vuejs/vue/compare/v2.0.0-beta.6...v2.0.0-beta.7) (2016-08-05)
-
-# [2.0.0-beta.6](https://github.com/vuejs/vue/compare/v2.0.0-beta.5...v2.0.0-beta.6) (2016-08-01)
-
-# [2.0.0-beta.5](https://github.com/vuejs/vue/compare/v2.0.0-beta.4...v2.0.0-beta.5) (2016-07-27)
-
-### Reverts
-
-- Revert "remove parser pre/post transforms (not used)" ([bee95f8](https://github.com/vuejs/vue/commit/bee95f8c08af2dd5af653847ec569bd801112c90))
-
-# [2.0.0-beta.4](https://github.com/vuejs/vue/compare/v2.0.0-beta.3...v2.0.0-beta.4) (2016-07-26)
-
-# [2.0.0-beta.3](https://github.com/vuejs/vue/compare/v2.0.0-beta.2...v2.0.0-beta.3) (2016-07-24)
-
-# [2.0.0-beta.2](https://github.com/vuejs/vue/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2016-07-17)
-
-# [2.0.0-beta.1](https://github.com/vuejs/vue/compare/v2.0.0-alpha.8...v2.0.0-beta.1) (2016-07-07)
-
-# [2.0.0-alpha.8](https://github.com/vuejs/vue/compare/v2.0.0-alpha.7...v2.0.0-alpha.8) (2016-06-28)
-
-# [2.0.0-alpha.7](https://github.com/vuejs/vue/compare/v2.0.0-alpha.6...v2.0.0-alpha.7) (2016-06-28)
-
-# [2.0.0-alpha.6](https://github.com/vuejs/vue/compare/v2.0.0-alpha.5...v2.0.0-alpha.6) (2016-06-22)
-
-# [2.0.0-alpha.5](https://github.com/vuejs/vue/compare/v2.0.0-alpha.4...v2.0.0-alpha.5) (2016-06-17)
-
-# [2.0.0-alpha.4](https://github.com/vuejs/vue/compare/v2.0.0-alpha.3...v2.0.0-alpha.4) (2016-06-16)
-
-# [2.0.0-alpha.3](https://github.com/vuejs/vue/compare/v2.0.0-alpha.2...v2.0.0-alpha.3) (2016-06-15)
-
-# [2.0.0-alpha.2](https://github.com/vuejs/vue/compare/v2.0.0-alpha.1...v2.0.0-alpha.2) (2016-06-13)
-
-# [2.0.0-alpha.1](https://github.com/vuejs/vue/compare/5b30f3eab89db88cb61894de617bdce53e82393e...v2.0.0-alpha.1) (2016-06-10)
-
-### Reverts
-
-- Revert "simplify array change detection" ([5b30f3e](https://github.com/vuejs/vue/commit/5b30f3eab89db88cb61894de617bdce53e82393e))
diff --git a/.github/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
similarity index 72%
rename from .github/CODE_OF_CONDUCT.md
rename to CODE_OF_CONDUCT.md
index 82effcdc869..7c82e0ca24f 100644
--- a/.github/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,8 +1,9 @@
-# Contributor Code of Conduct
+
+Contributor Code of Conduct
 
 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.
 
-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.
+We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
 
 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.
 
@@ -10,4 +11,4 @@ Project maintainers have the right and responsibility to remove, edit, or reject
 
 Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
 
-This Code of Conduct is adapted from the [Contributor Covenant](https://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/)
+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/)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000000..9a1867430e9
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,101 @@
+# Vue.js Contributing Guide
+
+Hi! I’m really excited that you are interested in contributing to Vue.js. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines.
+
+## Issue Reporting Guidelines
+
+- The issue list of this repo is **exclusively** for bug reports and feature requests. For simple questions, please use either [Gitter](https://gitter.im/vuejs/vue) or [the official forum](http://forum.vuejs.org/).
+
+- Try to search for your issue, it may have already been answered or even fixed in the development branch.
+
+- Check if the issue is reproducible with the latest stable version of Vue. If you are using a pre-release, please indicate the specific version you are using.
+
+- It is **required** that you clearly describe the steps necessary to reproduce the issue you are running into. Issues with no clear repro steps will not be triaged. If an issue labeled "need repro" receives no further input from the issue author for more than 5 days, it will be closed.
+
+- It is recommended that you make a JSFiddle/JSBin/Codepen to demonstrate your issue. You could start with [this template](http://jsfiddle.net/5sH6A/) that already includes the latest version of Vue.
+
+- For bugs that involves build setups, you can create a reproduction repository with steps in the README.
+
+- If your issue is resolved but still open, don’t hesitate to close it. In case you found a solution by yourself, it could be helpful to explain how you fixed it.
+
+## Pull Request Guidelines
+
+- Checkout a topic branch from `dev` and merge back against `dev`.
+
+- Work in the `src` folder and **DO NOT** checkin `dist` in the commits.
+
+- Squash the commit if there are too many small ones.
+
+- Follow the [code style](#code-style).
+
+- Make sure `npm test` passes. (see [development setup](#development-setup))
+
+- If adding new feature:
+    - Add accompanying test case.
+    - Provide convincing reason to add this feature. Ideally you should open a suggestion issue first and have it greenlighted before working on it.
+
+- If fixing a bug:
+    - Provide detailed description of the bug in the PR. Live demo preferred.
+    - Add appropriate test coverage if applicable.
+
+## Code Style
+
+- [No semicolons unless necessary](http://inimino.org/~inimino/blog/javascript_semicolons).
+
+- Follow JSDoc.
+
+- 2 spaces indentation.
+
+- multiple var declarations.
+
+- 1 space after `function` and function names.
+
+- 1 space between arguments, but not between parentheses.
+
+- Break long ternary conditionals like this:
+
+  ``` js
+  var a = superLongConditionalStatement
+    ? 'yep'
+    : 'nope'
+  ```
+
+- When in doubt, read the source code.
+
+## Development Setup
+
+You will need [Node.js](http://nodejs.org).
+
+``` bash
+$ npm install
+# optional: install pre-commit lint hook
+$ npm run install-hook
+```
+
+Dev mode: watch and auto-build `dist/vue.js` and serve unit tests at `http://localhost:8080` during development:
+
+``` bash
+$ npm run dev
+```
+
+To lint:
+
+``` bash
+$ npm run lint
+```
+
+To build:
+
+``` bash
+$ npm run build
+```
+
+Run default test suite:
+
+``` bash
+$ npm test
+```
+
+The default test script will do the following: lint -> unit tests with coverage -> build -> e2e tests. **Please make sure to have this pass successfully before submitting a PR.**
+
+The unit tests are written with [Jasmine](http://jasmine.github.io/2.3/introduction.html) and run with [Karma](http://karma-runner.github.io/0.13/index.html). The e2e tests are written for and run with [CasperJS](http://casperjs.org/).
diff --git a/LICENSE b/LICENSE
index b65dd9e62e2..f005cb1ff60 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2013-present, Yuxi (Evan) You
+Copyright (c) 2013-2016 Yuxi Evan You
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 274642ee3ae..cfb77f80044 100644
--- a/README.md
+++ b/README.md
@@ -1,122 +1,93 @@
-## Vue 2 has reached End of Life
-
-**You are looking at the now inactive repository for Vue 2. The actively maintained repository for the latest version of Vue is [vuejs/core](https://github.com/vuejs/core).**
-
-Vue has reached End of Life on December 31st, 2023. It no longer receives new features, updates, or fixes. However, it is still available on all existing distribution channels (CDNs, package managers, Github, etc).
-
-If you are starting a new project, please start with the latest version of Vue (3.x). We also strongly recommend current Vue 2 users to upgrade ([guide](https://v3-migration.vuejs.org/)), but we also acknowledge that not all users have the bandwidth or incentive to do so. If you have to stay on Vue 2 but also have compliance or security requirements about unmaintained software, check out [Vue 2 NES](https://www.herodevs.com/support/nes-vue?utm_source=vuejs-github&utm_medium=vue2-readme).
-
-<p align="center"><a href="https://vuejs.org" target="_blank" rel="noopener noreferrer"><img width="100" src="https://vuejs.org/images/logo.png" alt="Vue logo"></a></p>
+<p align="center"><a href="http://vuejs.org" target="_blank"><img width="100"src="http://vuejs.org/images/logo.png"></a></p>
 
 <p align="center">
-  <a href="https://circleci.com/gh/vuejs/vue/tree/dev"><img src="https://img.shields.io/circleci/project/github/vuejs/vue/dev.svg?sanitize=true" alt="Build Status"></a>
-  <a href="https://codecov.io/github/vuejs/vue?branch=dev"><img src="https://img.shields.io/codecov/c/github/vuejs/vue/dev.svg?sanitize=true" alt="Coverage Status"></a>
-  <a href="https://npmcharts.com/compare/vue?minimal=true"><img src="https://img.shields.io/npm/dm/vue.svg?sanitize=true" alt="Downloads"></a>
-  <a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/v/vue.svg?sanitize=true" alt="Version"></a>
-  <a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/l/vue.svg?sanitize=true" alt="License"></a>
-  <a href="https://chat.vuejs.org/"><img src="https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true" alt="Chat"></a>
+  <a href="https://circleci.com/gh/vuejs/vue/tree/master"><img src="https://img.shields.io/circleci/project/vuejs/vue/master.svg" alt="Build Status"></a>
+  <a href="https://codecov.io/github/vuejs/vue?branch=master"><img src="https://img.shields.io/codecov/c/github/vuejs/vue/dev.svg" alt="Coverage Status"></a>
+  <a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/dt/vue.svg" alt="Downloads"></a>
+  <a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/v/vue.svg" alt="Version"></a>
+  <a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/l/vue.svg" alt="License"></a>
+  <br>
+  <a href="https://saucelabs.com/u/vuejs"><img src="https://saucelabs.com/browser-matrix/vuejs.svg" alt="Sauce Test Status"></a>
 </p>
 
-## Sponsors
+## Supporting Vue.js
 
-Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome [backers](https://github.com/vuejs/core/blob/main/BACKERS.md). If you'd like to join them, please consider [ sponsor Vue's development](https://vuejs.org/sponsor/).
-
-<p align="center">
-  <h3 align="center">Special Sponsor</h3>
-</p>
+Vue.js is an MIT-licensed open source project. Its ongoing development is made possible thanks to the support by these awesome [backers](https://github.com/vuejs/vue/blob/dev/backers.md). If you'd like to join them, check out [Vue.js' Patreon campaign](https://www.patreon.com/evanyou).
 
 <p align="center">
-  <a target="_blank" href="https://github.com/appwrite/appwrite">
-  <img alt="special sponsor appwrite" src="https://sponsors.vuejs.org/images/appwrite.svg" width="300">
+  <b>Special thanks to the generous sponsorship by:</b>
+  <br><br>
+  <a href="http://www.itunescn.com/" style="">
+    <img width="120px" src="https://www.zymmm.com/content/images/2016/05/itunescn-logo-4.png">
   </a>
-</p>
-
-<p align="center">
-  <a target="_blank" href="https://vuejs.org/sponsor/">
-    <img alt="sponsors" src="https://sponsors.vuejs.org/sponsors.svg?v3">
+  <br><br>
+  <a href="https://jsfiddle.net/">
+    <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/jsfiddle.png">
+  </a>
+  <br><br>
+  <a href="https://laravel.com/">
+    <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/laravel.png">
+  </a>
+  <br><br>
+  <a href="https://chaitin.cn/">
+    <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/chaitin.png">
+  </a>
+  <br><br>
+  <a href="https://htmlburger.com/">
+    <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/htmlburger.png">
+  </a>
+  <br><br>
+  <a href="https://starter.someline.com/">
+    <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/someline.png">
+  </a>
+  <br><br>
+  <a href="http://gold.xitu.io/?utm_source=vuejs&utm_medium=image&utm_content=juejin&utm_campaign=q3_website">
+    <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/juejin.png">
+  </a>
+  <br><br>
+  <a href="http://monterail.com/" target="_blank">
+    <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/monterail.png">
+  </a>
+  <br><br>
+  <a href="https://www.trisoft.ro/" target="_blank">
+    <img width="120px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/trisoft.png">
   </a>
 </p>
 
----
-
-## Introduction
-
-Vue (pronounced `/vjuː/`, like view) is a **progressive framework** for building user interfaces. It is designed from the ground up to be incrementally adoptable, and can easily scale between a library and a framework depending on different use cases. It consists of an approachable core library that focuses on the view layer only, and an ecosystem of supporting libraries that helps you tackle complexity in large Single-Page Applications.
-
-#### Browser Compatibility
-
-Vue.js supports all browsers that are [ES5-compliant](https://compat-table.github.io/compat-table/es5/) (IE8 and below are not supported).
-
-## Ecosystem
-
-| Project               | Status                                                       | Description                                             |
-| --------------------- | ------------------------------------------------------------ | ------------------------------------------------------- |
-| [vue-router]          | [![vue-router-status]][vue-router-package]                   | Single-page application routing                         |
-| [vuex]                | [![vuex-status]][vuex-package]                               | Large-scale state management                            |
-| [vue-cli]             | [![vue-cli-status]][vue-cli-package]                         | Project scaffolding                                     |
-| [vue-loader]          | [![vue-loader-status]][vue-loader-package]                   | Single File Component (`*.vue` file) loader for webpack |
-| [vue-server-renderer] | [![vue-server-renderer-status]][vue-server-renderer-package] | Server-side rendering support                           |
-| [vue-class-component] | [![vue-class-component-status]][vue-class-component-package] | TypeScript decorator for a class-based API              |
-| [vue-rx]              | [![vue-rx-status]][vue-rx-package]                           | RxJS integration                                        |
-| [vue-devtools]        | [![vue-devtools-status]][vue-devtools-package]               | Browser DevTools extension                              |
-
-[vue-router]: https://github.com/vuejs/vue-router
-[vuex]: https://github.com/vuejs/vuex
-[vue-cli]: https://github.com/vuejs/vue-cli
-[vue-loader]: https://github.com/vuejs/vue-loader
-[vue-server-renderer]: https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer
-[vue-class-component]: https://github.com/vuejs/vue-class-component
-[vue-rx]: https://github.com/vuejs/vue-rx
-[vue-devtools]: https://github.com/vuejs/vue-devtools
-[vue-router-status]: https://img.shields.io/npm/v/vue-router.svg
-[vuex-status]: https://img.shields.io/npm/v/vuex.svg
-[vue-cli-status]: https://img.shields.io/npm/v/@vue/cli.svg
-[vue-loader-status]: https://img.shields.io/npm/v/vue-loader.svg
-[vue-server-renderer-status]: https://img.shields.io/npm/v/vue-server-renderer.svg
-[vue-class-component-status]: https://img.shields.io/npm/v/vue-class-component.svg
-[vue-rx-status]: https://img.shields.io/npm/v/vue-rx.svg
-[vue-devtools-status]: https://img.shields.io/chrome-web-store/v/nhdogjmejiglipccpnnnanhbledajbpd.svg
-[vue-router-package]: https://npmjs.com/package/vue-router
-[vuex-package]: https://npmjs.com/package/vuex
-[vue-cli-package]: https://npmjs.com/package/@vue/cli
-[vue-loader-package]: https://npmjs.com/package/vue-loader
-[vue-server-renderer-package]: https://npmjs.com/package/vue-server-renderer
-[vue-class-component-package]: https://npmjs.com/package/vue-class-component
-[vue-rx-package]: https://npmjs.com/package/vue-rx
-[vue-devtools-package]: https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd
-
-## Documentation
-
-To check out [live examples](https://v2.vuejs.org/v2/examples/) and docs, visit [vuejs.org](https://v2.vuejs.org).
+## Intro
 
-## Questions
+Vue.js is a library for building interactive web interfaces. It provides data-reactive components with a simple and flexible API. Core features include:
 
-For questions and support please use [the official forum](https://forum.vuejs.org) or [community chat](https://chat.vuejs.org/). The issue list of this repo is **exclusively** for bug reports and feature requests.
+- [Dead simple, unobtrusive reactivity using plain JavaScript objects.](http://vuejs.org/guide/overview.html#Reactive-Data-Binding)
+- [Component-oriented development style with tooling support](http://vuejs.org/guide/overview.html#Component-System)
+- Lean and extensible core
+- [Flexible transition effect system](http://vuejs.org/guide/transitions.html)
+- Fast without the need for complex optimization
 
-## Issues
-
-Please make sure to read the [Issue Reporting Checklist](https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
+Note that Vue.js only supports [ES5-compliant browsers](http://kangax.github.io/compat-table/es5/) (IE8 and below are not supported). To check out live examples and docs, visit [vuejs.org](http://vuejs.org).
 
-## Changelog
+## Questions
 
-Detailed changes for each release are documented in the [release notes](https://github.com/vuejs/vue/releases).
+For questions and support please use the [Gitter chat room](https://gitter.im/vuejs/vue) or [the official forum](http://forum.vuejs.org). The issue list of this repo is **exclusively** for bug reports and feature requests.
 
-## Stay In Touch
+## Issues
 
-- [Twitter](https://twitter.com/vuejs)
-- [Blog](https://medium.com/the-vue-point)
-- [Job Board](https://vuejobs.com/?ref=vuejs)
+Please make sure to read the [Issue Reporting Checklist](https://github.com/vuejs/vue/blob/dev/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
 
 ## Contribution
 
-Please make sure to read the [Contributing Guide](https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md) before making a pull request. If you have a Vue-related project/component/tool, add it with a pull request to [this curated list](https://github.com/vuejs/awesome-vue)!
+Please make sure to read the [Contributing Guide](https://github.com/vuejs/vue/blob/dev/CONTRIBUTING.md) before making a pull request. If you have a Vue-related project/component/tool, add it with a pull-request to [this curated list](https://github.com/vuejs/awesome-vue)!
 
-Thank you to all the people who already contributed to Vue!
+## Changelog
+
+Details changes for each release are documented in the [release notes](https://github.com/vuejs/vue/releases).
+
+## Stay In Touch
 
-<a href="https://github.com/vuejs/vue/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890" /></a>
+- For latest releases and announcements, follow on Twitter: [@vuejs](https://twitter.com/vuejs)
 
 ## License
 
-[MIT](https://opensource.org/licenses/MIT)
+[MIT](http://opensource.org/licenses/MIT)
 
-Copyright (c) 2013-present, Yuxi (Evan) You
+Copyright (c) 2013-2016 Evan You
diff --git a/api-extractor.json b/api-extractor.json
deleted file mode 100644
index b92115b8c62..00000000000
--- a/api-extractor.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
-  "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
-
-  "projectFolder": ".",
-
-  "compiler": {
-    "tsconfigFilePath": "api-extractor.tsconfig.json"
-  },
-
-  "mainEntryPointFilePath": "./temp/src/v3/index.d.ts",
-
-  "dtsRollup": {
-    "enabled": true,
-    "untrimmedFilePath": "",
-    "publicTrimmedFilePath": "./types/v3-generated.d.ts"
-  },
-
-  "apiReport": {
-    "enabled": false
-  },
-
-  "docModel": {
-    "enabled": false
-  },
-
-  "tsdocMetadata": {
-    "enabled": false
-  },
-
-  "messages": {
-    "compilerMessageReporting": {
-      "default": {
-        "logLevel": "warning"
-      }
-    },
-
-    "extractorMessageReporting": {
-      "default": {
-        "logLevel": "warning",
-        "addToApiReportFile": true
-      },
-
-      "ae-missing-release-tag": {
-        "logLevel": "none"
-      },
-      "ae-internal-missing-underscore": {
-        "logLevel": "none"
-      },
-      "ae-forgotten-export": {
-        "logLevel": "none"
-      }
-    },
-
-    "tsdocMessageReporting": {
-      "default": {
-        "logLevel": "warning"
-      },
-
-      "tsdoc-undefined-tag": {
-        "logLevel": "none"
-      }
-    }
-  }
-}
diff --git a/api-extractor.tsconfig.json b/api-extractor.tsconfig.json
deleted file mode 100644
index ba778b35435..00000000000
--- a/api-extractor.tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "extends": "./tsconfig.json",
-  "compilerOptions": {
-    "baseUrl": "./temp",
-    "types": []
-  }
-}
diff --git a/backers.md b/backers.md
new file mode 100644
index 00000000000..77495fb0f2d
--- /dev/null
+++ b/backers.md
@@ -0,0 +1,167 @@
+# Backers
+
+You can join them in supporting  Vue.js development by [pledging on Patreon](https://www.patreon.com/evanyou)! Backers in the same pledge level appear in the order of pledge date.
+
+### $2000
+
+[Currently vacant. It could be you!](https://www.patreon.com/bePatron?u=2341390&patAmt=2000.0&exp=1&rid=473784)
+
+---
+
+### $500
+
+<a href="http://www.itunescn.com/">
+  <img width="240px" src="https://www.zymmm.com/content/images/2016/05/itunescn-logo-4.png">
+</a>
+
+<a href="https://jsfiddle.net/">
+  <img width="240px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/jsfiddle.png">
+</a>
+
+<a href="https://laravel.com">
+  <img width="240px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/laravel.png">
+</a>
+
+<a href="https://chaitin.cn">
+  <img width="240px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/chaitin.png">
+</a>
+
+<a href="https://htmlburger.com/">
+  <img width="240px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/htmlburger.png">
+</a>
+
+<a href="https://starter.someline.com/">
+  <img width="240px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/someline.png">
+</a>
+
+<a href="http://gold.xitu.io/?utm_source=vuejs&utm_medium=image&utm_content=juejin&utm_campaign=q3_website">
+  <img width="180px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/juejin.png">
+</a>
+
+<a href="http://monterail.com/" target="_blank">
+  <img width="240px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/monterail.png">
+</a>
+
+<a href="https://www.trisoft.ro/" target="_blank">
+  <img width="240px" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/trisoft.png">
+</a>
+
+---
+
+### $250
+
+It could be you!
+
+---
+
+### $100
+
+<a href="http://tighten.co/">
+  <img width="200px" src="http://i.imgur.com/T7fQYLT.png">
+</a>
+
+<a href="http://invoicemachine.com/">
+  <img width="200px" src="http://assets.invoicemachine.com/images/flat_logo.png">
+</a>
+
+<a href="https://statamic.com/">
+  <img width="240px" src="http://i.imgur.com/MRIkKgp.png">
+</a>
+
+<a href="http://nodividestudio.com/">
+  <img width="100px" src="https://cloud.githubusercontent.com/assets/499550/16464256/552c2306-3e07-11e6-867f-e4f5ac64bace.png"> No Divide Studio
+</a>
+
+---
+
+### $50+
+
+- James Kyle
+- Blake Newman
+- Lee Smith
+- Adam Dorsey
+- Greg McCarvell
+- Yoshiya Hinosawa
+- Wasim Khamlichi
+- Jivan Roquet
+
+---
+
+### $10+
+
+- Sylvain Pollet-Villard
+- Luca Borghini
+- Kazuya Kawaguchi
+- Keisuke Kita
+- Anirudh Sanjeev
+- Guido Bertolino
+- Fábio Vedovelli
+- Jack Barham
+- Stephane Demoote
+- Paul R. Dillinger
+- Sean Washington
+- Alun Davey
+- Eduardo Kyvenko
+- Thijs de Maa
+- Joris Noordermeer
+- Niklas Lifors
+- An Phan
+- Richard Wyke
+- Roman Kuba
+- Tom Conlon
+- Matt Pickle
+- Simon East
+- Bill Columbia
+- Hayden Bickerton
+- Henry Zhu
+- John Smith
+- Benjamin Listwon
+- Rainer Morgan
+- Brian Jorden
+- Christopher Dosin
+- Lars Andreas Ness
+- Drew Lustro
+- Victor Tolbert
+- Jon Pokrzyk
+- Frank Dungan III
+- Lanes.io
+- Anders
+- Dexter Miguel
+- Stephen Michael Hartley
+- TJ Fogarty
+- Wen-Tien Chang
+- Ole Støvern
+- Valerian Cure
+- Dani Ilops
+- louisbl
+- Yegor Sytnyk
+- Guido H.
+- Joan Cejudo
+- Ian Walter
+- Nikola Trifunovic
+- Nicolas Mutis Mesa
+- Fahed Toumi
+- James
+- Spenser
+- Takuya Nishio
+- Daniel Diekmeier
+- Peter Thaleikis
+- Karol Fabjanczuk
+- Eduardo
+- Lê Chương
+- Webber Wang
+- Daniel Schmitz
+- Bruce Li
+- Mohammed
+- Sam Wainwright
+- TJ Hillard
+- Kyle Arrington
+- Jason Land
+- Miljan Aleksic
+- James Ye
+- Laurids Duellmann
+- Christo Crampton
+- Adon Metcalfe
+- Paul Straw
+- Jake Ingman
+- Eduardo Camillo
diff --git a/benchmarks/big-table/demo.css b/benchmarks/big-table/demo.css
deleted file mode 100644
index a83742c6bc7..00000000000
--- a/benchmarks/big-table/demo.css
+++ /dev/null
@@ -1,15 +0,0 @@
-form {
-  margin-bottom: 15px;
-}
-
-td.hidden {
-  color: #ccc;
-}
-
-table.filtered td.item {
-  background-color: #FFFFBF;
-}
-
-table.filtered td.item.hidden {
-  background-color: transparent;
-}
diff --git a/benchmarks/big-table/index.html b/benchmarks/big-table/index.html
deleted file mode 100644
index 156754683b0..00000000000
--- a/benchmarks/big-table/index.html
+++ /dev/null
@@ -1,163 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title></title>
-    <script src="../../dist/vue.min.js"></script>
-    <link rel="stylesheet" href="style.css">
-    <link rel="stylesheet" href="demo.css">
-  </head>
-  <body>
-    <div id="el">
-      <h1>Rendering Dynamic Big Table</h1>
-      <p>Reference: <a href="http://insin.github.io/ui-lib-samples/large-datasets/index.html">insin/ui-lib-samples/large-datasets</a></p>
-
-      <p>
-        <span>{{ rows }} x {{ cols }}, {{ optimized ? 'with' : 'without' }} optimization. {{ msg }}</span>
-      </p>
-
-      <p>
-        <button v-if="optimized" @click="loadBase">Disable optimization</button>
-        <button v-else @click="loadOptimized">Enable optimization (Object.freeze)</button>
-        <button @click="unmount">Unmount</button>
-        <button @click="rerender">Rerender with fresh data</button>
-      </p>
-
-      <form>
-        <strong>Filter Data</strong>:
-        <input type="text" v-model="filter">
-
-        <!--
-          If the user is filtering the data, we want to offer some insight into
-          the breadth of the filtering.
-        -->
-        <span v-if="filter">
-          &mdash;
-          Filtering <strong>{{ filter }}</strong>
-          over {{ dataPoints }} data points,
-          {{ visibleCount() }} found.
-        </span>
-
-      </form>
-
-      <table width="100%" cellspacing="2" :class="{ filtered: filter }">
-        <tr v-for="row in grid">
-          <th>{{ row.id }}</th>
-          <td v-for="item in row.items"
-            class="item"
-            :class="{ hidden: !matches(item) }">
-            {{ item.value }}
-          </td>
-        </tr>
-      </table>
-    </div>
-
-    <script>
-    var ROWS = 1000
-    var COLS = 10
-    var OPTIMIZED = window.location.hash === '#optimized'
-
-    window.onhashchange = function () {
-      window.location.reload()
-    }
-
-    function generateGrid( rowCount, columnCount ) {
-      var valuePoints = [
-        "Daenerys", "Jon", "Sansa", "Arya", "Stannis", "Gregor", "Tyrion",
-        "Theon", "Joffrey", "Ramsay", "Cersei", "Bran", "Margaery",
-        "Melisandre", "Daario", "Jamie", "Eddard", "Myrcella", "Robb",
-        "Jorah", "Petyr", "Tommen", "Sandor", "Oberyn", "Drogo", "Ygritte"
-      ]
-      var valueIndex = 0
-      var grid = []
-
-      for ( var r = 0; r < rowCount; r++ ) {
-        var row = {
-          id: r,
-          items: []
-        }
-        for ( var c = 0; c < columnCount; c++ ) {
-          row.items.push({
-            id: ( r + "-" + c ),
-            value: valuePoints[ valueIndex ]
-          })
-          if ( ++valueIndex >= valuePoints.length ) {
-            valueIndex = 0
-          }
-        }
-        grid.push(row)
-      }
-
-      return OPTIMIZED ? Object.freeze(grid) : grid
-    }
-
-    var grid = generateGrid(ROWS, COLS)
-    var s = window.performance.now()
-    console.profile('a')
-    var vm = new Vue({
-
-      el: '#el',
-
-      data: {
-        cols: COLS,
-        rows: ROWS,
-        optimized: OPTIMIZED,
-        msg: 'loading...',
-        grid: grid,
-        dataPoints: grid.length * grid[0].items.length,
-        filter: ''
-      },
-
-      methods: {
-        matches: function (item) {
-          return item.value.toLowerCase().indexOf(this.filter.toLowerCase()) > -1
-        },
-        visibleCount: function () {
-          var count = 0
-          var grid = this.grid
-          for (var i = 0, l = grid.length; i < l; i++) {
-            var row = grid[i].items
-            for (var j = 0, k = row.length; j < k; j++) {
-              var item = row[j]
-              var matched = !this.filter || this.matches(item)
-              if (matched) {
-                count++
-              }
-            }
-          }
-          return count
-        },
-        loadBase: function () {
-          window.location.hash = ''
-        },
-        loadOptimized: function () {
-          window.location.hash = '#optimized'
-        },
-        unmount: function () {
-          console.profile('unmount')
-          var s = window.performance.now()
-          this.grid = []
-          setTimeout(function () {
-            vm.msg = 'umount took: ' + (window.performance.now() - s).toFixed(2) + 'ms'
-            console.profileEnd('unmount')
-          }, 0)
-        },
-        rerender: function () {
-          var grid = generateGrid(1000, 10)
-          var s = window.performance.now()
-          console.profile('rerender')
-          this.grid = grid
-          setTimeout(function () {
-            vm.msg = 'rerender took: ' + (window.performance.now() - s).toFixed(2) + 'ms'
-            console.profileEnd('rerender')
-          }, 0)
-        }
-      }
-    })
-    console.profileEnd('a')
-    setTimeout(function () {
-      vm.msg = 'initial render took: ' + (window.performance.now() - s).toFixed(2) + 'ms'
-    }, 0)
-    </script>
-  </body>
-</html>
diff --git a/benchmarks/big-table/style.css b/benchmarks/big-table/style.css
deleted file mode 100644
index d58a91803d8..00000000000
--- a/benchmarks/big-table/style.css
+++ /dev/null
@@ -1,553 +0,0 @@
-@font-face {
-  font-family: octicons-anchor;
-  src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==) format('woff');
-}
-
-body  {
-  -webkit-text-size-adjust: 100%;
-  -ms-text-size-adjust: 100%;
-  text-size-adjust: 100%;
-  color: #333;
-  font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;
-  font-size: 16px;
-  line-height: 1.6;
-  word-wrap: break-word;
-  padding: 1em;
-}
-
-a {
-  background-color: transparent;
-}
-
-a:active,
-a:hover {
-  outline: 0;
-}
-
-strong {
-  font-weight: bold;
-}
-
-h1 {
-  font-size: 2em;
-  margin: 0.67em 0;
-}
-
-img {
-  border: 0;
-}
-
-hr {
-  box-sizing: content-box;
-  height: 0;
-}
-
-pre {
-  overflow: auto;
-}
-
-code,
-kbd,
-pre {
-  font-family: monospace, monospace;
-  font-size: 1em;
-}
-
-input {
-  color: inherit;
-  font: inherit;
-  margin: 0;
-}
-
-html input[disabled] {
-  cursor: default;
-}
-
-input {
-  line-height: normal;
-}
-
-input[type="checkbox"] {
-  box-sizing: border-box;
-  padding: 0;
-}
-
-table {
-  border-collapse: collapse;
-  border-spacing: 0;
-}
-
-td,
-th {
-  padding: 0;
-}
-
-* {
-  box-sizing: border-box;
-}
-
-input {
-  font: 13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
-}
-
-a {
-  color: #4078c0;
-  text-decoration: none;
-}
-
-a:hover,
-a:active {
-  text-decoration: underline;
-}
-
-hr {
-  height: 0;
-  margin: 15px 0;
-  overflow: hidden;
-  background: transparent;
-  border: 0;
-  border-bottom: 1px solid #ddd;
-}
-
-hr:before {
-  display: table;
-  content: "";
-}
-
-hr:after {
-  display: table;
-  clear: both;
-  content: "";
-}
-
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
-  margin-top: 15px;
-  margin-bottom: 15px;
-  line-height: 1.1;
-}
-
-h1 {
-  font-size: 30px;
-}
-h1:first-child {
-  margin-top: 0;
-}
-
-h2 {
-  font-size: 21px;
-}
-
-h3 {
-  font-size: 16px;
-}
-
-h4 {
-  font-size: 14px;
-}
-
-h5 {
-  font-size: 12px;
-}
-
-h6 {
-  font-size: 11px;
-}
-
-blockquote {
-  margin: 0;
-}
-
-ul,
-ol {
-  padding: 0;
-  margin-top: 0;
-  margin-bottom: 0;
-}
-
-ol ol,
-ul ol {
-  list-style-type: lower-roman;
-}
-
-ul ul ol,
-ul ol ol,
-ol ul ol,
-ol ol ol {
-  list-style-type: lower-alpha;
-}
-
-dd {
-  margin-left: 0;
-}
-
-code {
-  font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
-  font-size: 12px;
-}
-
-pre {
-  margin-top: 0;
-  margin-bottom: 0;
-  font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
-}
-
-.octicon {
-  font: normal normal normal 16px/1 octicons-anchor;
-  display: inline-block;
-  text-decoration: none;
-  text-rendering: auto;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  -ms-user-select: none;
-  user-select: none;
-}
-
-.octicon-link:before {
-  content: '\f05c';
-}
-
-.markdown-body>*:first-child {
-  margin-top: 0 !important;
-}
-
-.markdown-body>*:last-child {
-  margin-bottom: 0 !important;
-}
-
-a:not([href]) {
-  cursor: pointer;
-  text-decoration: none;
-}
-
-.anchor {
-  position: absolute;
-  top: 0;
-  left: 0;
-  display: block;
-  padding-right: 6px;
-  padding-left: 30px;
-  margin-left: -30px;
-}
-
-.anchor:focus {
-  outline: none;
-}
-
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
-  position: relative;
-  margin-top: 1em;
-  margin-bottom: 16px;
-  font-weight: bold;
-  line-height: 1.4;
-}
-
-h1 .octicon-link,
-h2 .octicon-link,
-h3 .octicon-link,
-h4 .octicon-link,
-h5 .octicon-link,
-h6 .octicon-link {
-  display: none;
-  color: #000;
-  vertical-align: middle;
-}
-
-h1:hover .anchor,
-h2:hover .anchor,
-h3:hover .anchor,
-h4:hover .anchor,
-h5:hover .anchor,
-h6:hover .anchor {
-  padding-left: 8px;
-  margin-left: -30px;
-  text-decoration: none;
-}
-
-h1:hover .anchor .octicon-link,
-h2:hover .anchor .octicon-link,
-h3:hover .anchor .octicon-link,
-h4:hover .anchor .octicon-link,
-h5:hover .anchor .octicon-link,
-h6:hover .anchor .octicon-link {
-  display: inline-block;
-}
-
-h1 {
-  padding-bottom: 0.3em;
-  font-size: 2.25em;
-  line-height: 1.2;
-  border-bottom: 1px solid #eee;
-}
-
-h1 .anchor {
-  line-height: 1;
-}
-
-h2 {
-  padding-bottom: 0.3em;
-  font-size: 1.75em;
-  line-height: 1.225;
-  border-bottom: 1px solid #eee;
-}
-
-h2 .anchor {
-  line-height: 1;
-}
-
-h3 {
-  font-size: 1.5em;
-  line-height: 1.43;
-}
-
-h3 .anchor {
-  line-height: 1.2;
-}
-
-h4 {
-  font-size: 1.25em;
-}
-
-h4 .anchor {
-  line-height: 1.2;
-}
-
-h5 {
-  font-size: 1em;
-}
-
-h5 .anchor {
-  line-height: 1.1;
-}
-
-h6 {
-  font-size: 1em;
-  color: #777;
-}
-
-h6 .anchor {
-  line-height: 1.1;
-}
-
-p,
-blockquote,
-ul,
-ol,
-dl,
-table,
-pre {
-  margin-top: 0;
-  margin-bottom: 16px;
-}
-
-hr {
-  height: 4px;
-  padding: 0;
-  margin: 16px 0;
-  background-color: #e7e7e7;
-  border: 0 none;
-}
-
-ul,
-ol {
-  padding-left: 2em;
-}
-
-ul ul,
-ul ol,
-ol ol,
-ol ul {
-  margin-top: 0;
-  margin-bottom: 0;
-}
-
-li>p {
-  margin-top: 16px;
-}
-
-dl {
-  padding: 0;
-}
-
-dl dt {
-  padding: 0;
-  margin-top: 16px;
-  font-size: 1em;
-  font-style: italic;
-  font-weight: bold;
-}
-
-dl dd {
-  padding: 0 16px;
-  margin-bottom: 16px;
-}
-
-blockquote {
-  padding: 0 15px;
-  color: #777;
-  border-left: 4px solid #ddd;
-}
-
-blockquote>:first-child {
-  margin-top: 0;
-}
-
-blockquote>:last-child {
-  margin-bottom: 0;
-}
-
-table {
-  display: block;
-  width: 100%;
-  overflow: auto;
-  word-break: normal;
-  word-break: keep-all;
-}
-
-table th {
-  font-weight: bold;
-}
-
-table th,
-table td {
-  padding: 6px 13px;
-  border: 1px solid #ddd;
-}
-
-table tr {
-  background-color: #fff;
-  border-top: 1px solid #ccc;
-}
-
-table tr:nth-child(2n) {
-  background-color: #f8f8f8;
-}
-
-img {
-  max-width: 100%;
-  box-sizing: border-box;
-}
-
-code {
-  padding: 0;
-  padding-top: 0.2em;
-  padding-bottom: 0.2em;
-  margin: 0;
-  font-size: 85%;
-  background-color: rgba(0,0,0,0.04);
-  border-radius: 3px;
-}
-
-code:before,
-code:after {
-  letter-spacing: -0.2em;
-  content: "\00a0";
-}
-
-pre>code {
-  padding: 0;
-  margin: 0;
-  font-size: 100%;
-  word-break: normal;
-  white-space: pre;
-  background: transparent;
-  border: 0;
-}
-
-.highlight {
-  margin-bottom: 16px;
-}
-
-.highlight pre,
-pre {
-  padding: 16px;
-  overflow: auto;
-  font-size: 85%;
-  line-height: 1.45;
-  background-color: #f7f7f7;
-  border-radius: 3px;
-}
-
-.highlight pre {
-  margin-bottom: 0;
-  word-break: normal;
-}
-
-pre {
-  word-wrap: normal;
-}
-
-pre code {
-  display: inline;
-  max-width: initial;
-  padding: 0;
-  margin: 0;
-  overflow: initial;
-  line-height: inherit;
-  word-wrap: normal;
-  background-color: transparent;
-  border: 0;
-}
-
-pre code:before,
-pre code:after {
-  content: normal;
-}
-
-kbd {
-  display: inline-block;
-  padding: 3px 5px;
-  font-size: 11px;
-  line-height: 10px;
-  color: #555;
-  vertical-align: middle;
-  background-color: #fcfcfc;
-  border: solid 1px #ccc;
-  border-bottom-color: #bbb;
-  border-radius: 3px;
-  box-shadow: inset 0 -1px 0 #bbb;
-}
-
-kbd {
-  display: inline-block;
-  padding: 3px 5px;
-  font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
-  line-height: 10px;
-  color: #555;
-  vertical-align: middle;
-  background-color: #fcfcfc;
-  border: solid 1px #ccc;
-  border-bottom-color: #bbb;
-  border-radius: 3px;
-  box-shadow: inset 0 -1px 0 #bbb;
-}
-
-.task-list-item {
-  list-style-type: none;
-}
-
-.task-list-item+.task-list-item {
-  margin-top: 3px;
-}
-
-.task-list-item input {
-  margin: 0 0.35em 0.25em -1.6em;
-  vertical-align: middle;
-}
-
-:checked+.radio-label {
-  z-index: 1;
-  position: relative;
-  border-color: #4078c0;
-}
diff --git a/benchmarks/dbmon/ENV.js b/benchmarks/dbmon/ENV.js
deleted file mode 100644
index 2559b7aa4fb..00000000000
--- a/benchmarks/dbmon/ENV.js
+++ /dev/null
@@ -1,211 +0,0 @@
-var ENV = ENV || (function() {
-
-  var first = true;
-  var counter = 0;
-  var data;
-  var _base;
-  (_base = String.prototype).lpad || (_base.lpad = function(padding, toLength) {
-    return padding.repeat((toLength - this.length) / padding.length).concat(this);
-  });
-
-  function formatElapsed(value) {
-    var str = parseFloat(value).toFixed(2);
-    if (value > 60) {
-      minutes = Math.floor(value / 60);
-      comps = (value % 60).toFixed(2).split('.');
-      seconds = comps[0].lpad('0', 2);
-      ms = comps[1];
-      str = minutes + ":" + seconds + "." + ms;
-    }
-    return str;
-  }
-
-  function getElapsedClassName(elapsed) {
-    var className = 'Query elapsed';
-    if (elapsed >= 10.0) {
-      className += ' warn_long';
-    }
-    else if (elapsed >= 1.0) {
-      className += ' warn';
-    }
-    else {
-      className += ' short';
-    }
-    return className;
-  }
-
-  function countClassName(queries) {
-    var countClassName = "label";
-    if (queries >= 20) {
-      countClassName += " label-important";
-    }
-    else if (queries >= 10) {
-      countClassName += " label-warning";
-    }
-    else {
-      countClassName += " label-success";
-    }
-    return countClassName;
-  }
-
-  function updateQuery(object) {
-    if (!object) {
-      object = {};
-    }
-    var elapsed = Math.random() * 15;
-    object.elapsed = elapsed;
-    object.formatElapsed = formatElapsed(elapsed);
-    object.elapsedClassName = getElapsedClassName(elapsed);
-    object.query = "SELECT blah FROM something";
-    object.waiting = Math.random() < 0.5;
-    if (Math.random() < 0.2) {
-      object.query = "<IDLE> in transaction";
-    }
-    if (Math.random() < 0.1) {
-      object.query = "vacuum";
-    }
-    return object;
-  }
-
-  function cleanQuery(value) {
-    if (value) {
-      value.formatElapsed = "";
-      value.elapsedClassName = "";
-      value.query = "";
-      value.elapsed = null;
-      value.waiting = null;
-    } else {
-      return {
-        query: "***",
-        formatElapsed: "",
-        elapsedClassName: ""
-      };
-    }
-  }
-
-  function generateRow(object, keepIdentity, counter) {
-    var nbQueries = Math.floor((Math.random() * 10) + 1);
-    if (!object) {
-      object = {};
-    }
-    object.lastMutationId = counter;
-    object.nbQueries = nbQueries;
-    if (!object.lastSample) {
-      object.lastSample = {};
-    }
-    if (!object.lastSample.topFiveQueries) {
-      object.lastSample.topFiveQueries = [];
-    }
-    if (keepIdentity) {
-      // for Angular optimization
-      if (!object.lastSample.queries) {
-        object.lastSample.queries = [];
-        for (var l = 0; l < 12; l++) {
-          object.lastSample.queries[l] = cleanQuery();
-        }
-      }
-      for (var j in object.lastSample.queries) {
-        var value = object.lastSample.queries[j];
-        if (j <= nbQueries) {
-          updateQuery(value);
-        } else {
-          cleanQuery(value);
-        }
-      }
-    } else {
-      object.lastSample.queries = [];
-      for (var j = 0; j < 12; j++) {
-        if (j < nbQueries) {
-          var value = updateQuery(cleanQuery());
-          object.lastSample.queries.push(value);
-        } else {
-          object.lastSample.queries.push(cleanQuery());
-        }
-      }
-    }
-    for (var i = 0; i < 5; i++) {
-      var source = object.lastSample.queries[i];
-      object.lastSample.topFiveQueries[i] = source;
-    }
-    object.lastSample.nbQueries = nbQueries;
-    object.lastSample.countClassName = countClassName(nbQueries);
-    return object;
-  }
-
-  function getData(keepIdentity) {
-    var oldData = data;
-    if (!keepIdentity) { // reset for each tick when !keepIdentity
-      data = [];
-      for (var i = 1; i <= ENV.rows; i++) {
-        data.push({ dbname: 'cluster' + i, query: "", formatElapsed: "", elapsedClassName: "" });
-        data.push({ dbname: 'cluster' + i + ' slave', query: "", formatElapsed: "", elapsedClassName: "" });
-      }
-    }
-    if (!data) { // first init when keepIdentity
-      data = [];
-      for (var i = 1; i <= ENV.rows; i++) {
-        data.push({ dbname: 'cluster' + i });
-        data.push({ dbname: 'cluster' + i + ' slave' });
-      }
-      oldData = data;
-    }
-    for (var i in data) {
-      var row = data[i];
-      if (!keepIdentity && oldData && oldData[i]) {
-        row.lastSample = oldData[i].lastSample;
-      }
-      if (!row.lastSample || Math.random() < ENV.mutations()) {
-        counter = counter + 1;
-        if (!keepIdentity) {
-          row.lastSample = null;
-        }
-        generateRow(row, keepIdentity, counter);
-      } else {
-        data[i] = oldData[i];
-      }
-    }
-    first = false;
-    return {
-      toArray: function() {
-        return data;
-      }
-    };
-  }
-
-  var mutationsValue = 0.5;
-
-  function mutations(value) {
-    if (value) {
-      mutationsValue = value;
-      return mutationsValue;
-    } else {
-      return mutationsValue;
-    }
-  }
-
-  var body = document.querySelector('body');
-  var theFirstChild = body.firstChild;
-
-  var sliderContainer = document.createElement( 'div' );
-  sliderContainer.style.cssText = "display: flex";
-  var slider = document.createElement('input');
-  var text = document.createElement('label');
-  text.innerHTML = 'mutations : ' + (mutationsValue * 100).toFixed(0) + '%';
-  text.id = "ratioval";
-  slider.setAttribute("type", "range");
-  slider.style.cssText = 'margin-bottom: 10px; margin-top: 5px';
-  slider.addEventListener('change', function(e) {
-    ENV.mutations(e.target.value / 100);
-    document.querySelector('#ratioval').innerHTML = 'mutations : ' + (ENV.mutations() * 100).toFixed(0) + '%';
-  });
-  sliderContainer.appendChild( text );
-  sliderContainer.appendChild( slider );
-  body.insertBefore( sliderContainer, theFirstChild );
-
-  return  {
-    generateData: getData,
-    rows: 50,
-    timeout: 0,
-    mutations: mutations
-  };
-})();
diff --git a/benchmarks/dbmon/app.js b/benchmarks/dbmon/app.js
deleted file mode 100644
index 61950d32f2b..00000000000
--- a/benchmarks/dbmon/app.js
+++ /dev/null
@@ -1,14 +0,0 @@
-var app = new Vue({
-  el: '#app',
-  data: {
-    databases: []
-  }
-})
-
-function loadSamples() {
-  app.databases = Object.freeze(ENV.generateData().toArray());
-  Monitoring.renderRate.ping();
-  setTimeout(loadSamples, ENV.timeout);
-}
-
-loadSamples()
diff --git a/benchmarks/dbmon/index.html b/benchmarks/dbmon/index.html
deleted file mode 100644
index e784bb4bfb6..00000000000
--- a/benchmarks/dbmon/index.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta name="description" content="dbmon vue" />
-<link href="./lib/styles.css" rel="stylesheet" type="text/css" />
-<title>dbmon (Vue)</title>
-</head>
-<body>
-  <h2>
-    Reference: <a href="http://mathieuancelin.github.io/js-repaint-perfs/">js-repaint-perfs</a>
-  </h2>
-  <div id="app">
-    <table class="table table-striped lastest-data">
-      <tbody>
-        <tr v-for="db in databases">
-          <td class="dbname">{{db.dbname}}</td>
-          <td class="query-count">
-            <span :class="db.lastSample.countClassName">{{db.lastSample.nbQueries}}</span>
-          </td>
-          <td v-for="q in db.lastSample.topFiveQueries" :class="'Query ' + q.elapsedClassName">
-            {{q.formatElapsed}}
-            <div class="popover left">
-              <div class="popover-content">{{q.query}}</div>
-              <div class="arrow"></div>
-            </div>
-          </td>
-        </tr>
-      </tbody>
-    </table>
-  </div>
-
-  <script src="./ENV.js"></script>
-  <script src="./lib/memory-stats.js"></script>
-  <script src="./lib/monitor.js"></script>
-  <script src="../../dist/vue.min.js"></script>
-  <script src="./app.js"></script>
-</body>
-</html>
diff --git a/benchmarks/dbmon/lib/memory-stats.js b/benchmarks/dbmon/lib/memory-stats.js
deleted file mode 100644
index 03272d897c0..00000000000
--- a/benchmarks/dbmon/lib/memory-stats.js
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * @author mrdoob / http://mrdoob.com/
- * @author jetienne / http://jetienne.com/
- * @author paulirish / http://paulirish.com/
- */
-var MemoryStats = function (){
-
-	var msMin	= 100;
-	var msMax	= 0;
-
-	var container	= document.createElement( 'div' );
-	container.id	= 'stats';
-	container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer';
-
-	var msDiv	= document.createElement( 'div' );
-	msDiv.id	= 'ms';
-	msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;';
-	container.appendChild( msDiv );
-
-	var msText	= document.createElement( 'div' );
-	msText.id	= 'msText';
-	msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
-	msText.innerHTML= 'Memory';
-	msDiv.appendChild( msText );
-
-	var msGraph	= document.createElement( 'div' );
-	msGraph.id	= 'msGraph';
-	msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0';
-	msDiv.appendChild( msGraph );
-
-	while ( msGraph.children.length < 74 ) {
-
-		var bar = document.createElement( 'span' );
-		bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131';
-		msGraph.appendChild( bar );
-
-	}
-
-	var updateGraph = function ( dom, height, color ) {
-
-		var child = dom.appendChild( dom.firstChild );
-		child.style.height = height + 'px';
-		if( color ) child.style.backgroundColor = color;
-
-	}
-
-	var perf = window.performance || {};
-	// polyfill usedJSHeapSize
-	if (!perf.memory){
-		perf.memory = { usedJSHeapSize : 0 };
-	}
-
-	// support of the API?
-	if( perf.memory.totalJSHeapSize === 0 ){
-		console.warn('totalJSHeapSize === 0... performance.memory is only available in Chrome .')
-	}
-
-	// TODO, add a sanity check to see if values are bucketed.
-	// If so, remind user to adopt the --enable-precise-memory-info flag.
-	// open -a "/Applications/Google Chrome.app" --args --enable-precise-memory-info
-
-	var lastTime	= Date.now();
-	var lastUsedHeap= perf.memory.usedJSHeapSize;
-	return {
-		domElement: container,
-
-		update: function () {
-
-			// refresh only 30time per second
-			if( Date.now() - lastTime < 1000/30 )	return;
-			lastTime	= Date.now()
-
-			var delta	= perf.memory.usedJSHeapSize - lastUsedHeap;
-			lastUsedHeap	= perf.memory.usedJSHeapSize;
-			var color	= delta < 0 ? '#830' : '#131';
-
-			var ms	= perf.memory.usedJSHeapSize;
-			msMin	= Math.min( msMin, ms );
-			msMax	= Math.max( msMax, ms );
-			msText.textContent = "Mem: " + bytesToSize(ms, 2);
-
-			var normValue	= ms / (30*1024*1024);
-			var height	= Math.min( 30, 30 - normValue * 30 );
-			updateGraph( msGraph, height, color);
-
-			function bytesToSize( bytes, nFractDigit ){
-				var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
-				if (bytes == 0) return 'n/a';
-				nFractDigit	= nFractDigit !== undefined ? nFractDigit : 0;
-				var precision	= Math.pow(10, nFractDigit);
-				var i 		= Math.floor(Math.log(bytes) / Math.log(1024));
-				return Math.round(bytes*precision / Math.pow(1024, i))/precision + ' ' + sizes[i];
-			};
-		}
-
-	}
-
-};
diff --git a/benchmarks/dbmon/lib/monitor.js b/benchmarks/dbmon/lib/monitor.js
deleted file mode 100644
index dccad19c1b3..00000000000
--- a/benchmarks/dbmon/lib/monitor.js
+++ /dev/null
@@ -1,60 +0,0 @@
-var Monitoring = Monitoring || (function() {
-
-  var stats = new MemoryStats();
-  stats.domElement.style.position = 'fixed';
-  stats.domElement.style.right        = '0px';
-  stats.domElement.style.bottom       = '0px';
-  document.body.appendChild( stats.domElement );
-  requestAnimationFrame(function rAFloop(){
-      stats.update();
-      requestAnimationFrame(rAFloop);
-  });
-
-  var RenderRate = function () {
-    var container = document.createElement( 'div' );
-    container.id  = 'stats';
-    container.style.cssText = 'width:150px;opacity:0.9;cursor:pointer;position:fixed;right:80px;bottom:0px;';
-
-    var msDiv = document.createElement( 'div' );
-    msDiv.id  = 'ms';
-    msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;';
-    container.appendChild( msDiv );
-
-    var msText  = document.createElement( 'div' );
-    msText.id = 'msText';
-    msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px';
-    msText.innerHTML= 'Repaint rate: 0/sec';
-    msDiv.appendChild( msText );
-
-    var bucketSize = 20;
-    var bucket = [];
-    var lastTime  = Date.now();
-    return {
-      domElement: container,
-      ping: function () {
-        var start = lastTime;
-        var stop = Date.now();
-        var rate = 1000 / (stop - start);
-        bucket.push(rate);
-        if (bucket.length > bucketSize) {
-          bucket.shift();
-        }
-        var sum = 0;
-        for (var i = 0; i < bucket.length; i++) {
-          sum = sum + bucket[i];
-        }
-        msText.textContent = "Repaint rate: " + (sum / bucket.length).toFixed(2) + "/sec";
-        lastTime = stop;
-      }
-    }
-  };
-
-  var renderRate = new RenderRate();
-  document.body.appendChild( renderRate.domElement );
-
-  return {
-    memoryStats: stats,
-    renderRate: renderRate
-  };
-  
-})();
diff --git a/benchmarks/dbmon/lib/styles.css b/benchmarks/dbmon/lib/styles.css
deleted file mode 100644
index da43dd939dd..00000000000
--- a/benchmarks/dbmon/lib/styles.css
+++ /dev/null
@@ -1,26 +0,0 @@
-body {color:#333;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;margin:0;}
-label {display:inline-block;font-weight:700;margin-bottom:5px;}
-input[type=range] {display:block;width:100%;}
-table {border-collapse:collapse;border-spacing:0;}
-:before,:after {box-sizing: border-box;}
-
-.table > thead > tr > th,.table > tbody > tr > th,.table > tfoot > tr > th,.table > thead > tr > td,.table > tbody > tr > td,.table > tfoot > tr > td {border-top:1px solid #ddd;line-height:1.42857143;padding:8px;vertical-align:top;}
-.table {width:100%;}
-.table-striped > tbody > tr:nth-child(odd) > td,.table-striped > tbody > tr:nth-child(odd) > th {background:#f9f9f9;}
-
-.label {border-radius:.25em;color:#fff;display:inline;font-size:75%;font-weight:700;line-height:1;padding:.2em .6em .3em;text-align:center;vertical-align:baseline;white-space:nowrap;}
-.label-success {background-color:#5cb85c;}
-.label-warning {background-color:#f0ad4e;}
-
-.popover {background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,.2);display:none;left:0;max-width:276px;padding:1px;position:absolute;text-align:left;top:0;white-space:normal;z-index:1010;}
-.popover>.arrow:after {border-width:10px;content:"";}
-.popover.left {margin-left:-10px;}
-.popover.left > .arrow {border-right-width:0;border-left-color:rgba(0,0,0,.25);margin-top:-11px;right:-11px;top:50%;}
-.popover.left > .arrow:after {border-left-color:#fff;border-right-width:0;bottom:-10px;content:" ";right:1px;}
-.popover > .arrow {border-width:11px;}
-.popover > .arrow,.popover>.arrow:after {border-color:transparent;border-style:solid;display:block;height:0;position:absolute;width:0;}
-
-.popover-content {padding:9px 14px;}
-
-.Query {position:relative;}
-.Query:hover .popover {display:block;left:-100%;width:100%;}
diff --git a/benchmarks/reorder-list/index.html b/benchmarks/reorder-list/index.html
deleted file mode 100644
index 668160b0719..00000000000
--- a/benchmarks/reorder-list/index.html
+++ /dev/null
@@ -1,111 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title>Vue benchmark</title>
-  </head>
-  <body>
-    <script src="https://cdn.jsdelivr.net/lodash/4.10.0/lodash.min.js"></script>
-    <script src="../../dist/vue.min.js"></script>
-    <style>
-    .danger {
-      background-color: red;
-    }
-    </style>
-
-    <script type="text/x-template" id="t">
-      <div>
-        <h1>{{ items.length }} Components</h1>
-        <p>{{ action }} took {{time}}ms.</p>
-        <button @click="shuffle">shuffle</button>
-        <button @click="add">add</button>
-        <table class="table table-hover table-striped test-data">
-          <row v-for="item in items" :key="item.id"
-            :class="{ danger: item.id === selected }"
-            :item="item"
-            @select="select(item)"
-            @remove="remove(item)">
-          </row>
-        </table>
-      </div>
-    </script>
-
-    <script type="text/x-template" id="row">
-      <tr>
-        <td class="col-md-1">{{item.id}}</td>
-        <td class="col-md-4">
-            <a @click="$emit('select')">{{item.label}}</a>
-        </td>
-        <td class="col-md-1">
-          <button @click="$emit('remove')">remove</button>
-        </td>
-      </tr>
-    </script>
-
-    <div id="el">
-    </div>
-
-    <script>
-    var total = 1000
-    var items = []
-    for (var i = 0; i < total; i++) {
-      items.push({
-        id: i,
-        label: String(Math.random()).slice(0, 5)
-      })
-    }
-
-    var s = window.performance.now()
-    console.profile('render')
-    var vm = new Vue({
-      el: '#el',
-      template: '#t',
-      data: {
-        total: total,
-        time: 0,
-        action: 'Render',
-        items: items,
-        selected: null
-      },
-      methods: {
-        shuffle: monitor('shuffle', function () {
-          this.items = _.shuffle(this.items)
-        }),
-        add: monitor('add', function () {
-          this.items.push({
-            id: total++,
-            label: String(Math.random()).slice(0, 5)
-          })
-        }),
-        select: monitor('select', function (item) {
-          this.selected = item.id
-        }),
-        remove: monitor('remove', function (item) {
-          this.items.splice(this.items.indexOf(item), 1)
-        })
-      },
-      components: {
-        row: {
-          props: ['item'],
-          template: '#row'
-        }
-      }
-    })
-    setTimeout(function () {
-      vm.time = window.performance.now() - s
-      console.profileEnd('render')
-    }, 0)
-
-    function monitor (action, fn) {
-      return function () {
-        var s = window.performance.now()
-        fn.apply(this, arguments)
-        Vue.nextTick(function () {
-          vm.action = action
-          vm.time = window.performance.now() - s
-        })
-      }
-    }
-    </script>
-  </body>
-</html>
diff --git a/benchmarks/ssr/README.md b/benchmarks/ssr/README.md
deleted file mode 100644
index c61d6daa57d..00000000000
--- a/benchmarks/ssr/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Vue.js SSR benchmark
-
-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`.
-
-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.
-
-`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.
-
-### run
-
-``` bash
-npm run bench:ssr
-```
diff --git a/benchmarks/ssr/common.js b/benchmarks/ssr/common.js
deleted file mode 100644
index 0411ac896dc..00000000000
--- a/benchmarks/ssr/common.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict'
-
-const self = (global || root) // eslint-disable-line
-
-self.performance = {
-  now: function () {
-    var hrtime = process.hrtime()
-    return ((hrtime[0] * 1000000 + hrtime[1] / 1000) / 1000)
-  }
-}
-
-function generateGrid (rowCount, columnCount) {
-  var grid = []
-
-  for (var r = 0; r < rowCount; r++) {
-    var row = { id: r, items: [] }
-    for (var c = 0; c < columnCount; c++) {
-      row.items.push({ id: (r + '-' + c) })
-    }
-    grid.push(row)
-  }
-
-  return grid
-}
-
-const gridData = generateGrid(1000, 10)
-
-module.exports = {
-  template: '<div><h1>{{ Math.random() }}</h1><my-table></my-table></div>',
-  components: {
-    myTable: {
-      data: function () {
-        return {
-          grid: gridData
-        }
-      },
-      // template: '<table><tr v-for="row in grid"><th>123</th><td v-for="item in row.items">{{ item.id }}</td></tr></table>',
-      template: '<table width="100%" cellspacing="2"><row v-for="row in grid" :key="row.id" :row="row"></row></table>',
-      components: {
-        row: {
-          props: ['row'],
-          template: '<tr><th>{{ Math.random() }}</th><column v-for="item in row.items" :key="item.id"></column></tr>',
-          components: {
-            column: {
-              template: '<td class="item">' +
-                // 25 plain elements for each cell
-                '<ul class="yoyo">' +
-                  `<li v-for="i in 5" :class="'hihi' + i">` +
-                    `<span :id="i + '_' + j" v-for="j in 5">fsefs</span>` +
-                    '</li>' +
-                '</ul>' +
-              '</td>'
-            }
-          }
-        }
-      }
-    }
-  }
-}
diff --git a/benchmarks/ssr/renderToStream.js b/benchmarks/ssr/renderToStream.js
deleted file mode 100644
index 40dacef7811..00000000000
--- a/benchmarks/ssr/renderToStream.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/* eslint-disable no-unused-vars */
-
-'use strict'
-
-process.env.NODE_ENV = 'production'
-
-const Vue = require('../../dist/vue.runtime.common.js')
-const createRenderer = require('../../packages/server-renderer').createRenderer
-const renderToStream = createRenderer().renderToStream
-const gridComponent = require('./common.js')
-
-console.log('--- renderToStream --- ')
-const self = (global || root)
-const s = self.performance.now()
-
-const stream = renderToStream(new Vue(gridComponent))
-let str = ''
-let first
-let complete
-stream.once('data', () => {
-  first = self.performance.now() - s
-})
-stream.on('data', chunk => {
-  str += chunk
-})
-stream.on('end', () => {
-  complete = self.performance.now() - s
-  console.log(`first chunk: ${first.toFixed(2)}ms`)
-  console.log(`complete: ${complete.toFixed(2)}ms`)
-  console.log()
-})
diff --git a/benchmarks/ssr/renderToString.js b/benchmarks/ssr/renderToString.js
deleted file mode 100644
index 1988f730e73..00000000000
--- a/benchmarks/ssr/renderToString.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict'
-
-process.env.NODE_ENV = 'production'
-
-const Vue = require('../../dist/vue.runtime.common.js')
-const createRenderer = require('../../packages/server-renderer').createRenderer
-const renderToString = createRenderer().renderToString
-const gridComponent = require('./common.js')
-
-console.log('--- renderToString --- ')
-const self = (global || root)
-self.s = self.performance.now()
-
-renderToString(new Vue(gridComponent), (err, res) => {
-  if (err) throw err
-  // console.log(res)
-  console.log('Complete time: ' + (self.performance.now() - self.s).toFixed(2) + 'ms')
-  console.log()
-})
diff --git a/benchmarks/svg/index.html b/benchmarks/svg/index.html
deleted file mode 100644
index cd274cbfcea..00000000000
--- a/benchmarks/svg/index.html
+++ /dev/null
@@ -1,102 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <title>vue.js version</title>
-  <script src="https://cdn.jsdelivr.net/stats.js/r11/stats.min.js"></script>
-  <script src="../../dist/vue.min.js"></script>
-  <style>
-  html, body {
-    height: 100%;
-    width: 100%;
-    padding: 0;
-    margin: 0;
-  }
-  svg {
-    width: 800px;
-    height: 600px;
-  }
-  </style>
-</head>
-<body>
-  <h1>Animating 1000 SVG dots</h1>
-  <div id="app">
-    <p>
-      <button @click="toggleOptimization">
-        {{ optimized ? 'disable' : 'enable' }} optimization (Object.freeze)
-      </button>
-    </p>
-    <svg>
-      <circle  v-for='point in model.points' :cx='point.x' :cy='point.y' r='2px' fill='#FC309D'></circle>
-    </svg>
-  </div>
-<script type="text/javascript" charset="utf-8">
-var stats = new Stats()
-stats.setMode(0)
-stats.domElement.style.position = 'absolute'
-stats.domElement.style.right = '0px'
-stats.domElement.style.top = '0px'
-document.body.appendChild(stats.domElement)
-
-var WIDTH = 800
-var HEIGHT = 600
-
-new Vue({
-  el: '#app',
-  data: {
-    model: createModel(1000),
-    optimized: false
-  },
-  created: function () {
-    var self = this
-    requestAnimationFrame(render)
-    stats.begin()
-    function render () {
-      stats.end()
-      stats.begin()
-      requestAnimationFrame(render)
-      self.model.step()
-      if (self.optimized) {
-        self.$forceUpdate()
-      }
-    }
-  },
-  methods: {
-    toggleOptimization: function () {
-      this.model = this.optimized
-        ? createModel(1000)
-        : Object.freeze(createModel(1000))
-      this.optimized = !this.optimized
-    }
-  }
-});
-
-function createModel (count) {
-  var points = []
-  for (var i = 0; i < count; ++i) {
-    points.push({
-      x: Math.random() * WIDTH,
-      y: Math.random() * HEIGHT,
-      vx: Math.random() * 4 - 2,
-      vy: Math.random() * 4 - 2
-    })
-  }
-
-  return {
-    points: points,
-    step: step
-  }
-
-  function step () {
-    points.forEach(move)
-  }
-
-  function move (p) {
-    if (p.x > WIDTH || p.x < 0) p.vx *= -1
-    if (p.y > HEIGHT || p.y < 0) p.vy *= -1
-    p.y += p.vy
-    p.x += p.vx
-  }
-}
-</script>
-</body>
-</html>
diff --git a/benchmarks/uptime/index.html b/benchmarks/uptime/index.html
deleted file mode 100644
index d43c93010a6..00000000000
--- a/benchmarks/uptime/index.html
+++ /dev/null
@@ -1,200 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title>Vue benchmark</title>
-    <style type="text/css">
-      html, body {
-        margin: 0;
-        padding: 0 10px;
-        font-family: sans-serif;
-      }
-
-      #fps {
-        position: fixed;
-        top: 0px;
-        right: 0px;
-        padding: 32px;
-        font-size: 32px;
-        text-align: right;
-      }
-
-      * {
-        box-sizing: border-box;
-      }
-
-      .server-uptime {
-        display: block;
-        overflow: hidden;
-        margin: 0 auto;
-        width: 50%;
-      }
-
-      .server-uptime + .server-uptime {
-        margin: 20px auto 0 auto;
-        border-top: 1px solid #999;
-      }
-
-      .days {
-        display: flex;
-        flex-direction: row;
-        flex-wrap: wrap;
-      }
-
-      .uptime-day {
-        display: flex;
-      }
-
-      span.uptime-day-status {
-        width: 10px;
-        height: 10px;
-        margin: 1px;
-      }
-
-      .hover {
-        display: none;
-      }
-
-      .uptime-day-status:hover + .hover {
-        display: flex;
-        position: absolute;
-        margin-top: -35px;
-        margin-left: -30px;
-        border-radius: 4px;
-        color: #eee;
-        background-color: #333;
-        padding: 10px;
-        font-size: 11px;
-      }
-    </style>
-  </head>
-  <body>
-    <p>Reference: <a href="https://github.com/tildeio/glimmer/blob/master/packages/glimmer-demos/lib/uptime.ts">Ember Glimmer 2 demo</a></p>
-    <div id="app">
-      <p>FPS: {{ fps }}</p>
-      <button @click="toggle">{{ playing ? 'pause' : 'play' }}</button>
-      <server-uptime
-        v-for="server in servers"
-        :key="server.name"
-        :name="server.name"
-        :days="server.days">
-      </server-uptime>
-    </div>
-    <script src="../../dist/vue.min.js"></script>
-    <script>
-      // functional components are perfect for small, presentational components
-      // and they are much more efficient than stateful ones.
-      Vue.component('uptime-day', {
-        props: ['day'],
-        functional: true,
-        render (h, ctx) {
-          var day = ctx.props.day
-          return h('div', { staticClass: 'uptime-day'}, [
-            h('span', { staticClass: 'uptime-day-status', style: { backgroundColor: day.up ? '#8cc665' : '#ccc' } }),
-            h('span', { staticClass: 'hover' }, [day.number + ': ' + day.up ? 'Servers operational!' : 'Red alert!'])
-          ])
-        }
-      })
-
-      Vue.component('server-uptime', {
-        props: ['name', 'days'],
-        computed: {
-          upDays () {
-            return this.days.reduce(function (upDays, day) {
-              return upDays += (day.up ? 1 : 0)
-            }, 0)
-          },
-          maxStreak () {
-            var streak = this.days.reduce(([max, streak], day) => {
-              if (day.up && streak + 1 > max) {
-                return [streak + 1, streak + 1]
-              } else if (day.up) {
-                return [max, streak + 1]
-              } else {
-                return [max, 0]
-              }
-            }, [0, 0])
-
-            return streak.max
-          }
-        },
-        template: `
-          <div class="server-uptime">
-            <h1>{{name}}</h1>
-            <h2>{{upDays}} Days Up</h2>
-            <h2>Biggest Streak: {{maxStreak}}</h2>
-            <div class="days">
-              <uptime-day
-                v-for="day in days"
-                :key="day.number"
-                :day="day">
-              </uptime-day>
-            </div>
-          </div>
-        `
-      })
-
-      function generateServer (name) {
-        var days = []
-        for (var i=0; i<=364; i++) {
-          var up = Math.random() > 0.2
-          days.push({ number: i, up })
-        }
-        return { name, days }
-      }
-
-      function generateServers () {
-        return [
-          generateServer("Stefan's Server"),
-          generateServer("Godfrey's Server"),
-          generateServer("Yehuda's Server")
-        ]
-      }
-
-      var s = window.performance.now()
-      var app = new Vue({
-        el: '#app',
-        data: {
-          fps: 0,
-          playing: false,
-          servers: Object.freeze(generateServers())
-        },
-        methods: {
-          toggle () {
-            this.playing = !this.playing
-            if (this.playing) {
-              update()
-            } else {
-              clearTimeout(timeoutId)
-            }
-          }
-        }
-      })
-      console.log('initial render: ' + (window.performance.now() - s) + 'ms')
-
-      var fpsMeter = {
-        alpha: 2/121,
-        lastValue: null,
-        push (dataPoint) {
-          if (this.lastValue) {
-            return this.lastValue = this.lastValue + this.alpha * (dataPoint - this.lastValue)
-          } else {
-            return this.lastValue = dataPoint
-          }
-        }
-      }
-
-      var timeoutId
-      var lastFrame = null
-      function update () {
-        var thisFrame = window.performance.now()
-        if (lastFrame) {
-          app.fps = Math.round(fpsMeter.push(1000 / (thisFrame - lastFrame)))
-        }
-        app.servers = Object.freeze(generateServers())
-        timeoutId = setTimeout(update, 0) // not using rAF because that limits us to 60fps!
-        lastFrame = thisFrame
-      }
-    </script>
-  </body>
-</html>
diff --git a/bower.json b/bower.json
new file mode 100644
index 00000000000..1d9a4baad84
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,19 @@
+{
+  "name": "vue",
+  "main": "dist/vue.js",
+  "description": "Simple, Fast & Composable MVVM for building interative interfaces",
+  "authors": ["Evan You <yyx990803@gmail.com>"],
+  "license": "MIT",
+  "ignore": [
+    ".*",
+    "examples",
+    "build",
+    "perf",
+    "test",
+    "grunt",
+    "gruntfile.js",
+    "*.json",
+    "*.md",
+    "*.yml"
+  ]
+}
diff --git a/build/build.js b/build/build.js
new file mode 100644
index 00000000000..d9685aace38
--- /dev/null
+++ b/build/build.js
@@ -0,0 +1,133 @@
+var fs = require('fs')
+var zlib = require('zlib')
+var rollup = require('rollup')
+var uglify = require('uglify-js')
+var babel = require('rollup-plugin-babel')
+var replace = require('rollup-plugin-replace')
+var version = process.env.VERSION || require('../package.json').version
+
+var banner =
+  '/*!\n' +
+  ' * Vue.js v' + version + '\n' +
+  ' * (c) ' + new Date().getFullYear() + ' Evan You\n' +
+  ' * Released under the MIT License.\n' +
+  ' */'
+
+// update main file
+var main = fs
+  .readFileSync('src/index.js', 'utf-8')
+  .replace(/Vue\.version = '[\d\.]+'/, "Vue.version = '" + version + "'")
+fs.writeFileSync('src/index.js', main)
+
+// CommonJS build.
+// this is used as the "main" field in package.json
+// and used by bundlers like Webpack and Browserify.
+rollup.rollup({
+  entry: 'src/index.js',
+  plugins: [
+    babel({
+      loose: 'all'
+    })
+  ]
+})
+.then(function (bundle) {
+  return write('dist/vue.common.js', bundle.generate({
+    format: 'cjs',
+    banner: banner
+  }).code)
+})
+// Standalone Dev Build
+.then(function () {
+  return rollup.rollup({
+    entry: 'src/index.js',
+    plugins: [
+      replace({
+        'process.env.NODE_ENV': "'development'"
+      }),
+      babel({
+        loose: 'all'
+      })
+    ]
+  })
+  .then(function (bundle) {
+    return write('dist/vue.js', bundle.generate({
+      format: 'umd',
+      banner: banner,
+      moduleName: 'Vue'
+    }).code)
+  })
+})
+.then(function () {
+  // Standalone Production Build
+  return rollup.rollup({
+    entry: 'src/index.js',
+    plugins: [
+      replace({
+        'process.env.NODE_ENV': "'production'"
+      }),
+      babel({
+        loose: 'all'
+      })
+    ]
+  })
+  .then(function (bundle) {
+    var code = bundle.generate({
+      format: 'umd',
+      moduleName: 'Vue',
+      banner: banner
+    }).code
+    var res = uglify.minify(code, {
+      fromString: true,
+      outSourceMap: 'vue.min.js.map',
+      output: {
+        preamble: banner,
+        ascii_only: true
+      }
+    })
+    // fix uglifyjs sourcemap
+    var map = JSON.parse(res.map)
+    map.sources = ['vue.js']
+    map.sourcesContent = [code]
+    map.file = 'vue.min.js'
+    return [
+      write('dist/vue.min.js', res.code),
+      write('dist/vue.min.js.map', JSON.stringify(map))
+    ]
+  })
+  .then(zip)
+})
+.catch(logError)
+
+function write (dest, code) {
+  return new Promise(function (resolve, reject) {
+    fs.writeFile(dest, code, function (err) {
+      if (err) return reject(err)
+      console.log(blue(dest) + ' ' + getSize(code))
+      resolve()
+    })
+  })
+}
+
+function zip () {
+  return new Promise(function (resolve, reject) {
+    fs.readFile('dist/vue.min.js', function (err, buf) {
+      if (err) return reject(err)
+      zlib.gzip(buf, function (err, buf) {
+        if (err) return reject(err)
+        write('dist/vue.min.js.gz', buf).then(resolve)
+      })
+    })
+  })
+}
+
+function getSize (code) {
+  return (code.length / 1024).toFixed(2) + 'kb'
+}
+
+function logError (e) {
+  console.log(e)
+}
+
+function blue (str) {
+  return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m'
+}
diff --git a/build/ci.sh b/build/ci.sh
new file mode 100755
index 00000000000..4f8fa463b82
--- /dev/null
+++ b/build/ci.sh
@@ -0,0 +1,11 @@
+set -e
+if [[ -z $CI_PULL_REQUEST ]] && [[ $CIRCLE_BRANCH = master ]]; then
+  npm run lint
+  npm run cover
+  cat ./coverage/lcov.info | ./node_modules/.bin/codecov
+  npm run build
+  npm run e2e
+  npm run sauce-all
+else
+  npm test
+fi
diff --git a/build/git-hooks/pre-commit b/build/git-hooks/pre-commit
new file mode 100755
index 00000000000..e3885ffb4b1
--- /dev/null
+++ b/build/git-hooks/pre-commit
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+# get files to be linted
+FILES=$(git diff --cached --name-only | grep -E '^src|^test/unit/specs|^test/e2e')
+
+# lint them if any
+if [[ $FILES ]]; then
+  ./node_modules/.bin/eslint $FILES
+fi
diff --git a/build/karma.base.config.js b/build/karma.base.config.js
new file mode 100644
index 00000000000..111983f0cda
--- /dev/null
+++ b/build/karma.base.config.js
@@ -0,0 +1,20 @@
+var webpackConfig = require('./webpack.test.config')
+delete webpackConfig.entry
+webpackConfig.devtool = 'inline-source-map'
+
+// shared config for all unit tests
+module.exports = {
+  frameworks: ['jasmine'],
+  files: [
+    '../test/unit/lib/jquery.js',
+    '../test/unit/specs/index.js'
+  ],
+  preprocessors: {
+    '../test/unit/specs/index.js': ['webpack', 'sourcemap']
+  },
+  webpack: webpackConfig,
+  webpackMiddleware: {
+    noInfo: true
+  },
+  singleRun: true
+}
diff --git a/build/karma.cover.config.js b/build/karma.cover.config.js
new file mode 100644
index 00000000000..1353b463a97
--- /dev/null
+++ b/build/karma.cover.config.js
@@ -0,0 +1,26 @@
+var assign = require('object-assign')
+var base = require('./karma.base.config.js')
+
+module.exports = function (config) {
+  var options = assign(base, {
+    browsers: ['PhantomJS'],
+    reporters: ['progress', 'coverage'],
+    coverageReporter: {
+      reporters: [
+        { type: 'lcov', dir: '../coverage', subdir: '.' },
+        { type: 'text-summary', dir: '../coverage', subdir: '.' }
+      ]
+    }
+  })
+
+  // add coverage post loader
+  options.webpack.module.postLoaders = [
+    {
+      test: /\.js$/,
+      exclude: /test|node_modules/,
+      loader: 'istanbul-instrumenter'
+    }
+  ]
+
+  config.set(options)
+}
diff --git a/build/karma.sauce.config.js b/build/karma.sauce.config.js
new file mode 100644
index 00000000000..092372d13c9
--- /dev/null
+++ b/build/karma.sauce.config.js
@@ -0,0 +1,82 @@
+var assign = require('object-assign')
+var base = require('./karma.base.config.js')
+
+/**
+ * Having too many tests running concurrently on saucelabs
+ * causes timeouts and errors, so we have to run them in
+ * smaller batches.
+ */
+
+var batches = [
+  // the cool kids
+  {
+    sl_chrome: {
+      base: 'SauceLabs',
+      browserName: 'chrome',
+      platform: 'Windows 7'
+    },
+    sl_firefox: {
+      base: 'SauceLabs',
+      browserName: 'firefox'
+    },
+    sl_mac_safari: {
+      base: 'SauceLabs',
+      browserName: 'safari',
+      platform: 'OS X 10.10'
+    }
+  },
+  // ie family
+  {
+    sl_ie_9: {
+      base: 'SauceLabs',
+      browserName: 'internet explorer',
+      platform: 'Windows 7',
+      version: '9'
+    },
+    sl_ie_10: {
+      base: 'SauceLabs',
+      browserName: 'internet explorer',
+      platform: 'Windows 8',
+      version: '10'
+    },
+    sl_ie_11: {
+      base: 'SauceLabs',
+      browserName: 'internet explorer',
+      platform: 'Windows 8.1',
+      version: '11'
+    }
+  },
+  // mobile
+  {
+    sl_ios_safari: {
+      base: 'SauceLabs',
+      browserName: 'iphone',
+      platform: 'OS X 10.9',
+      version: '7.1'
+    },
+    sl_android: {
+      base: 'SauceLabs',
+      browserName: 'android',
+      platform: 'Linux',
+      version: '4.2'
+    }
+  }
+]
+
+module.exports = function (config) {
+  var batch = batches[process.argv[4] || 0]
+
+  config.set(assign(base, {
+    browsers: Object.keys(batch),
+    customLaunchers: batch,
+    reporters: ['progress', 'saucelabs'],
+    sauceLabs: {
+      testName: 'Vue.js unit tests',
+      recordScreenshots: false,
+      build: process.env.CIRCLE_BUILD_NUM || process.env.SAUCE_BUILD_ID || Date.now()
+    },
+    // mobile emulators are really slow
+    captureTimeout: 300000,
+    browserNoActivityTimeout: 300000
+  }))
+}
diff --git a/build/karma.unit.config.js b/build/karma.unit.config.js
new file mode 100644
index 00000000000..e2c83e21d2b
--- /dev/null
+++ b/build/karma.unit.config.js
@@ -0,0 +1,9 @@
+var assign = require('object-assign')
+var base = require('./karma.base.config.js')
+
+module.exports = function (config) {
+  config.set(assign(base, {
+    browsers: ['Chrome', 'Firefox', 'Safari'],
+    reporters: ['progress']
+  }))
+}
diff --git a/build/release-csp.sh b/build/release-csp.sh
new file mode 100644
index 00000000000..569af790a70
--- /dev/null
+++ b/build/release-csp.sh
@@ -0,0 +1,21 @@
+# get versions
+PLAIN_VERSION=`npm version | grep vue | sed -e 's/[^0-9.]//g'`
+CSP_VERSION=$PLAIN_VERSION-csp
+
+# update package.json
+sed -i '' -e 's/\("version"\: "[0-9]*\.[0-9]*\.[0-9]*\)"/\1-csp"/' package.json
+
+# test + build
+npm test
+
+# push to csp branch on github
+git add -A .
+git commit -m "[build] $CSP_VERSION"
+git push -f
+
+# push tag
+git tag v$CSP_VERSION
+git push origin v$CSP_VERSION
+
+# publish to npm
+npm publish --tag csp
diff --git a/build/release.sh b/build/release.sh
new file mode 100644
index 00000000000..c22578143b5
--- /dev/null
+++ b/build/release.sh
@@ -0,0 +1,33 @@
+set -e
+echo "Enter release version: "
+read VERSION
+
+read -p "Releasing $VERSION - are you sure? (y/n)" -n 1 -r
+echo    # (optional) move to a new line
+if [[ $REPLY =~ ^[Yy]$ ]]
+then
+  echo "Releasing $VERSION ..."
+
+  # lint and test
+  npm run lint 2>/dev/null
+  npm run unit 2>/dev/null
+  npm run cover 2>/dev/null
+
+  # build
+  VERSION=$VERSION npm run build
+
+  # e2e
+  npm run e2e 2>/dev/null
+  # sauce
+  npm run sauce-all 2>/dev/null
+
+  # commit
+  git add -A
+  git commit -m "[build] $VERSION"
+  npm version $VERSION --message "[release] $VERSION"
+
+  # publish
+  git push origin refs/tags/v$VERSION
+  git push
+  npm publish
+fi
diff --git a/build/webpack.dev.config.js b/build/webpack.dev.config.js
new file mode 100644
index 00000000000..aead8970da6
--- /dev/null
+++ b/build/webpack.dev.config.js
@@ -0,0 +1,28 @@
+var path = require('path')
+var webpack = require('webpack')
+
+module.exports = {
+  entry: './src/index',
+  output: {
+    path: path.resolve(__dirname, '../dist'),
+    filename: 'vue.js',
+    library: 'Vue',
+    libraryTarget: 'umd'
+  },
+  module: {
+    loaders: [
+      { test: /\.js$/, loader: 'babel' }
+    ]
+  },
+  babel: {
+    loose: 'all'
+  },
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': {
+        NODE_ENV: '"development"'
+      }
+    })
+  ],
+  devtool: 'source-map'
+}
diff --git a/build/webpack.test.config.js b/build/webpack.test.config.js
new file mode 100644
index 00000000000..e694b4971e2
--- /dev/null
+++ b/build/webpack.test.config.js
@@ -0,0 +1,45 @@
+var path = require('path')
+var webpack = require('webpack')
+
+module.exports = {
+  entry: './test/unit/specs/index.js',
+  output: {
+    path: path.resolve(__dirname, '../test/unit'),
+    filename: 'specs.js'
+  },
+  resolve: {
+    alias: {
+      src: path.resolve(__dirname, '../src')
+    }
+  },
+  module: {
+    loaders: [
+      {
+        test: /\.js$/,
+        loader: 'babel',
+        // NOTE: use absolute path to make sure
+        // running tests is OK even if it is in node_modules of other project
+        exclude: [
+          path.resolve(__dirname, '../test/unit'),
+          path.resolve(__dirname, '../node_modules')
+        ]
+      }
+    ]
+  },
+  babel: {
+    loose: 'all',
+    optional: ['runtime']
+  },
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': {
+        NODE_ENV: '"development"'
+      }
+    })
+  ],
+  devServer: {
+    contentBase: './test/unit',
+    noInfo: true
+  },
+  devtool: 'source-map'
+}
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 00000000000..8de97ff9122
--- /dev/null
+++ b/circle.yml
@@ -0,0 +1,7 @@
+machine:
+  node:
+    version: 5
+
+test:
+  override:
+    - bash ./build/ci.sh
diff --git a/compiler-sfc/index.d.ts b/compiler-sfc/index.d.ts
deleted file mode 100644
index 3c30abc8ccf..00000000000
--- a/compiler-sfc/index.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from '@vue/compiler-sfc'
diff --git a/compiler-sfc/index.js b/compiler-sfc/index.js
deleted file mode 100644
index 774f9da2742..00000000000
--- a/compiler-sfc/index.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('@vue/compiler-sfc')
diff --git a/compiler-sfc/index.mjs b/compiler-sfc/index.mjs
deleted file mode 100644
index 3c30abc8ccf..00000000000
--- a/compiler-sfc/index.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export * from '@vue/compiler-sfc'
diff --git a/compiler-sfc/package.json b/compiler-sfc/package.json
deleted file mode 100644
index 778c7ebf51c..00000000000
--- a/compiler-sfc/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "main": "index.js",
-  "module": "index.mjs",
-  "types": "index.d.ts"
-}
diff --git a/dist/README.md b/dist/README.md
new file mode 100644
index 00000000000..f6de4719ea1
--- /dev/null
+++ b/dist/README.md
@@ -0,0 +1,3 @@
+# NOTE!
+
+The `dist` folder contains the standalone build for Vue.js, however files here are only checked-in when a release happens. If you are on the `dev` branch, files here are **NOT** up to date. Only the `master` branch contains the built files for the latest stable version.
\ No newline at end of file
diff --git a/dist/vue.common.js b/dist/vue.common.js
index 3bbc37c95fc..63ebe385546 100644
--- a/dist/vue.common.js
+++ b/dist/vue.common.js
@@ -1,5 +1,10238 @@
-if (process.env.NODE_ENV === 'production') {
-  module.exports = require('./vue.common.prod.js')
+/*!
+ * Vue.js v1.0.28
+ * (c) 2016 Evan You
+ * Released under the MIT License.
+ */
+'use strict';
+
+function set(obj, key, val) {
+  if (hasOwn(obj, key)) {
+    obj[key] = val;
+    return;
+  }
+  if (obj._isVue) {
+    set(obj._data, key, val);
+    return;
+  }
+  var ob = obj.__ob__;
+  if (!ob) {
+    obj[key] = val;
+    return;
+  }
+  ob.convert(key, val);
+  ob.dep.notify();
+  if (ob.vms) {
+    var i = ob.vms.length;
+    while (i--) {
+      var vm = ob.vms[i];
+      vm._proxy(key);
+      vm._digest();
+    }
+  }
+  return val;
+}
+
+/**
+ * Delete a property and trigger change if necessary.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ */
+
+function del(obj, key) {
+  if (!hasOwn(obj, key)) {
+    return;
+  }
+  delete obj[key];
+  var ob = obj.__ob__;
+  if (!ob) {
+    if (obj._isVue) {
+      delete obj._data[key];
+      obj._digest();
+    }
+    return;
+  }
+  ob.dep.notify();
+  if (ob.vms) {
+    var i = ob.vms.length;
+    while (i--) {
+      var vm = ob.vms[i];
+      vm._unproxy(key);
+      vm._digest();
+    }
+  }
+}
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+/**
+ * Check whether the object has the property.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @return {Boolean}
+ */
+
+function hasOwn(obj, key) {
+  return hasOwnProperty.call(obj, key);
+}
+
+/**
+ * Check if an expression is a literal value.
+ *
+ * @param {String} exp
+ * @return {Boolean}
+ */
+
+var literalValueRE = /^\s?(true|false|-?[\d\.]+|'[^']*'|"[^"]*")\s?$/;
+
+function isLiteral(exp) {
+  return literalValueRE.test(exp);
+}
+
+/**
+ * Check if a string starts with $ or _
+ *
+ * @param {String} str
+ * @return {Boolean}
+ */
+
+function isReserved(str) {
+  var c = (str + '').charCodeAt(0);
+  return c === 0x24 || c === 0x5F;
+}
+
+/**
+ * Guard text output, make sure undefined outputs
+ * empty string
+ *
+ * @param {*} value
+ * @return {String}
+ */
+
+function _toString(value) {
+  return value == null ? '' : value.toString();
+}
+
+/**
+ * Check and convert possible numeric strings to numbers
+ * before setting back to data
+ *
+ * @param {*} value
+ * @return {*|Number}
+ */
+
+function toNumber(value) {
+  if (typeof value !== 'string') {
+    return value;
+  } else {
+    var parsed = Number(value);
+    return isNaN(parsed) ? value : parsed;
+  }
+}
+
+/**
+ * Convert string boolean literals into real booleans.
+ *
+ * @param {*} value
+ * @return {*|Boolean}
+ */
+
+function toBoolean(value) {
+  return value === 'true' ? true : value === 'false' ? false : value;
+}
+
+/**
+ * Strip quotes from a string
+ *
+ * @param {String} str
+ * @return {String | false}
+ */
+
+function stripQuotes(str) {
+  var a = str.charCodeAt(0);
+  var b = str.charCodeAt(str.length - 1);
+  return a === b && (a === 0x22 || a === 0x27) ? str.slice(1, -1) : str;
+}
+
+/**
+ * Camelize a hyphen-delimited string.
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+var camelizeRE = /-(\w)/g;
+
+function camelize(str) {
+  return str.replace(camelizeRE, toUpper);
+}
+
+function toUpper(_, c) {
+  return c ? c.toUpperCase() : '';
+}
+
+/**
+ * Hyphenate a camelCase string.
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+var hyphenateRE = /([^-])([A-Z])/g;
+
+function hyphenate(str) {
+  return str.replace(hyphenateRE, '$1-$2').replace(hyphenateRE, '$1-$2').toLowerCase();
+}
+
+/**
+ * Converts hyphen/underscore/slash delimitered names into
+ * camelized classNames.
+ *
+ * e.g. my-component => MyComponent
+ *      some_else    => SomeElse
+ *      some/comp    => SomeComp
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+var classifyRE = /(?:^|[-_\/])(\w)/g;
+
+function classify(str) {
+  return str.replace(classifyRE, toUpper);
+}
+
+/**
+ * Simple bind, faster than native
+ *
+ * @param {Function} fn
+ * @param {Object} ctx
+ * @return {Function}
+ */
+
+function bind(fn, ctx) {
+  return function (a) {
+    var l = arguments.length;
+    return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx);
+  };
+}
+
+/**
+ * Convert an Array-like object to a real Array.
+ *
+ * @param {Array-like} list
+ * @param {Number} [start] - start index
+ * @return {Array}
+ */
+
+function toArray(list, start) {
+  start = start || 0;
+  var i = list.length - start;
+  var ret = new Array(i);
+  while (i--) {
+    ret[i] = list[i + start];
+  }
+  return ret;
+}
+
+/**
+ * Mix properties into target object.
+ *
+ * @param {Object} to
+ * @param {Object} from
+ */
+
+function extend(to, from) {
+  var keys = Object.keys(from);
+  var i = keys.length;
+  while (i--) {
+    to[keys[i]] = from[keys[i]];
+  }
+  return to;
+}
+
+/**
+ * Quick object check - this is primarily used to tell
+ * Objects from primitive values when we know the value
+ * is a JSON-compliant type.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+function isObject(obj) {
+  return obj !== null && typeof obj === 'object';
+}
+
+/**
+ * Strict object type check. Only returns true
+ * for plain JavaScript objects.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+var toString = Object.prototype.toString;
+var OBJECT_STRING = '[object Object]';
+
+function isPlainObject(obj) {
+  return toString.call(obj) === OBJECT_STRING;
+}
+
+/**
+ * Array type check.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+var isArray = Array.isArray;
+
+/**
+ * Define a property.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {*} val
+ * @param {Boolean} [enumerable]
+ */
+
+function def(obj, key, val, enumerable) {
+  Object.defineProperty(obj, key, {
+    value: val,
+    enumerable: !!enumerable,
+    writable: true,
+    configurable: true
+  });
+}
+
+/**
+ * Debounce a function so it only gets called after the
+ * input stops arriving after the given wait period.
+ *
+ * @param {Function} func
+ * @param {Number} wait
+ * @return {Function} - the debounced function
+ */
+
+function _debounce(func, wait) {
+  var timeout, args, context, timestamp, result;
+  var later = function later() {
+    var last = Date.now() - timestamp;
+    if (last < wait && last >= 0) {
+      timeout = setTimeout(later, wait - last);
+    } else {
+      timeout = null;
+      result = func.apply(context, args);
+      if (!timeout) context = args = null;
+    }
+  };
+  return function () {
+    context = this;
+    args = arguments;
+    timestamp = Date.now();
+    if (!timeout) {
+      timeout = setTimeout(later, wait);
+    }
+    return result;
+  };
+}
+
+/**
+ * Manual indexOf because it's slightly faster than
+ * native.
+ *
+ * @param {Array} arr
+ * @param {*} obj
+ */
+
+function indexOf(arr, obj) {
+  var i = arr.length;
+  while (i--) {
+    if (arr[i] === obj) return i;
+  }
+  return -1;
+}
+
+/**
+ * Make a cancellable version of an async callback.
+ *
+ * @param {Function} fn
+ * @return {Function}
+ */
+
+function cancellable(fn) {
+  var cb = function cb() {
+    if (!cb.cancelled) {
+      return fn.apply(this, arguments);
+    }
+  };
+  cb.cancel = function () {
+    cb.cancelled = true;
+  };
+  return cb;
+}
+
+/**
+ * Check if two values are loosely equal - that is,
+ * if they are plain objects, do they have the same shape?
+ *
+ * @param {*} a
+ * @param {*} b
+ * @return {Boolean}
+ */
+
+function looseEqual(a, b) {
+  /* eslint-disable eqeqeq */
+  return a == b || (isObject(a) && isObject(b) ? JSON.stringify(a) === JSON.stringify(b) : false);
+  /* eslint-enable eqeqeq */
+}
+
+var hasProto = ('__proto__' in {});
+
+// Browser environment sniffing
+var inBrowser = typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]';
+
+// detect devtools
+var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
+
+// UA sniffing for working around browser-specific quirks
+var UA = inBrowser && window.navigator.userAgent.toLowerCase();
+var isIE = UA && UA.indexOf('trident') > 0;
+var isIE9 = UA && UA.indexOf('msie 9.0') > 0;
+var isAndroid = UA && UA.indexOf('android') > 0;
+var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA);
+
+var transitionProp = undefined;
+var transitionEndEvent = undefined;
+var animationProp = undefined;
+var animationEndEvent = undefined;
+
+// Transition property/event sniffing
+if (inBrowser && !isIE9) {
+  var isWebkitTrans = window.ontransitionend === undefined && window.onwebkittransitionend !== undefined;
+  var isWebkitAnim = window.onanimationend === undefined && window.onwebkitanimationend !== undefined;
+  transitionProp = isWebkitTrans ? 'WebkitTransition' : 'transition';
+  transitionEndEvent = isWebkitTrans ? 'webkitTransitionEnd' : 'transitionend';
+  animationProp = isWebkitAnim ? 'WebkitAnimation' : 'animation';
+  animationEndEvent = isWebkitAnim ? 'webkitAnimationEnd' : 'animationend';
+}
+
+/* istanbul ignore next */
+function isNative(Ctor) {
+  return (/native code/.test(Ctor.toString())
+  );
+}
+
+/**
+ * Defer a task to execute it asynchronously. Ideally this
+ * should be executed as a microtask, so we leverage
+ * MutationObserver if it's available, and fallback to
+ * setTimeout(0).
+ *
+ * @param {Function} cb
+ * @param {Object} ctx
+ */
+
+var nextTick = (function () {
+  var callbacks = [];
+  var pending = false;
+  var timerFunc = undefined;
+
+  function nextTickHandler() {
+    pending = false;
+    var copies = callbacks.slice(0);
+    callbacks.length = 0;
+    for (var i = 0; i < copies.length; i++) {
+      copies[i]();
+    }
+  }
+
+  // the nextTick behavior leverages the microtask queue, which can be accessed
+  // via either native Promise.then or MutationObserver.
+  // MutationObserver has wider support, however it is seriously bugged in
+  // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
+  // completely stops working after triggering a few times... so, if native
+  // Promise is available, we will use it:
+  /* istanbul ignore if */
+  if (typeof Promise !== 'undefined' && isNative(Promise)) {
+    var p = Promise.resolve();
+    var noop = function noop() {};
+    timerFunc = function () {
+      p.then(nextTickHandler);
+      // in problematic UIWebViews, Promise.then doesn't completely break, but
+      // it can get stuck in a weird state where callbacks are pushed into the
+      // microtask queue but the queue isn't being flushed, until the browser
+      // needs to do some other work, e.g. handle a timer. Therefore we can
+      // "force" the microtask queue to be flushed by adding an empty timer.
+      if (isIOS) setTimeout(noop);
+    };
+  } else if (typeof MutationObserver !== 'undefined') {
+    // use MutationObserver where native Promise is not available,
+    // e.g. IE11, iOS7, Android 4.4
+    var counter = 1;
+    var observer = new MutationObserver(nextTickHandler);
+    var textNode = document.createTextNode(String(counter));
+    observer.observe(textNode, {
+      characterData: true
+    });
+    timerFunc = function () {
+      counter = (counter + 1) % 2;
+      textNode.data = String(counter);
+    };
+  } else {
+    // fallback to setTimeout
+    /* istanbul ignore next */
+    timerFunc = setTimeout;
+  }
+
+  return function (cb, ctx) {
+    var func = ctx ? function () {
+      cb.call(ctx);
+    } : cb;
+    callbacks.push(func);
+    if (pending) return;
+    pending = true;
+    timerFunc(nextTickHandler, 0);
+  };
+})();
+
+var _Set = undefined;
+/* istanbul ignore if */
+if (typeof Set !== 'undefined' && isNative(Set)) {
+  // use native Set when available.
+  _Set = Set;
 } else {
-  module.exports = require('./vue.common.dev.js')
+  // a non-standard Set polyfill that only works with primitive keys.
+  _Set = function () {
+    this.set = Object.create(null);
+  };
+  _Set.prototype.has = function (key) {
+    return this.set[key] !== undefined;
+  };
+  _Set.prototype.add = function (key) {
+    this.set[key] = 1;
+  };
+  _Set.prototype.clear = function () {
+    this.set = Object.create(null);
+  };
+}
+
+function Cache(limit) {
+  this.size = 0;
+  this.limit = limit;
+  this.head = this.tail = undefined;
+  this._keymap = Object.create(null);
+}
+
+var p = Cache.prototype;
+
+/**
+ * Put <value> into the cache associated with <key>.
+ * Returns the entry which was removed to make room for
+ * the new entry. Otherwise undefined is returned.
+ * (i.e. if there was enough room already).
+ *
+ * @param {String} key
+ * @param {*} value
+ * @return {Entry|undefined}
+ */
+
+p.put = function (key, value) {
+  var removed;
+
+  var entry = this.get(key, true);
+  if (!entry) {
+    if (this.size === this.limit) {
+      removed = this.shift();
+    }
+    entry = {
+      key: key
+    };
+    this._keymap[key] = entry;
+    if (this.tail) {
+      this.tail.newer = entry;
+      entry.older = this.tail;
+    } else {
+      this.head = entry;
+    }
+    this.tail = entry;
+    this.size++;
+  }
+  entry.value = value;
+
+  return removed;
+};
+
+/**
+ * Purge the least recently used (oldest) entry from the
+ * cache. Returns the removed entry or undefined if the
+ * cache was empty.
+ */
+
+p.shift = function () {
+  var entry = this.head;
+  if (entry) {
+    this.head = this.head.newer;
+    this.head.older = undefined;
+    entry.newer = entry.older = undefined;
+    this._keymap[entry.key] = undefined;
+    this.size--;
+  }
+  return entry;
+};
+
+/**
+ * Get and register recent use of <key>. Returns the value
+ * associated with <key> or undefined if not in cache.
+ *
+ * @param {String} key
+ * @param {Boolean} returnEntry
+ * @return {Entry|*}
+ */
+
+p.get = function (key, returnEntry) {
+  var entry = this._keymap[key];
+  if (entry === undefined) return;
+  if (entry === this.tail) {
+    return returnEntry ? entry : entry.value;
+  }
+  // HEAD--------------TAIL
+  //   <.older   .newer>
+  //  <--- add direction --
+  //   A  B  C  <D>  E
+  if (entry.newer) {
+    if (entry === this.head) {
+      this.head = entry.newer;
+    }
+    entry.newer.older = entry.older; // C <-- E.
+  }
+  if (entry.older) {
+    entry.older.newer = entry.newer; // C. --> E
+  }
+  entry.newer = undefined; // D --x
+  entry.older = this.tail; // D. --> E
+  if (this.tail) {
+    this.tail.newer = entry; // E. <-- D
+  }
+  this.tail = entry;
+  return returnEntry ? entry : entry.value;
+};
+
+var cache$1 = new Cache(1000);
+var reservedArgRE = /^in$|^-?\d+/;
+
+/**
+ * Parser state
+ */
+
+var str;
+var dir;
+var len;
+var index;
+var chr;
+var state;
+var startState = 0;
+var filterState = 1;
+var filterNameState = 2;
+var filterArgState = 3;
+
+var doubleChr = 0x22;
+var singleChr = 0x27;
+var pipeChr = 0x7C;
+var escapeChr = 0x5C;
+var spaceChr = 0x20;
+
+var expStartChr = { 0x5B: 1, 0x7B: 1, 0x28: 1 };
+var expChrPair = { 0x5B: 0x5D, 0x7B: 0x7D, 0x28: 0x29 };
+
+function peek() {
+  return str.charCodeAt(index + 1);
+}
+
+function next() {
+  return str.charCodeAt(++index);
+}
+
+function eof() {
+  return index >= len;
+}
+
+function eatSpace() {
+  while (peek() === spaceChr) {
+    next();
+  }
+}
+
+function isStringStart(chr) {
+  return chr === doubleChr || chr === singleChr;
+}
+
+function isExpStart(chr) {
+  return expStartChr[chr];
+}
+
+function isExpEnd(start, chr) {
+  return expChrPair[start] === chr;
+}
+
+function parseString() {
+  var stringQuote = next();
+  var chr;
+  while (!eof()) {
+    chr = next();
+    // escape char
+    if (chr === escapeChr) {
+      next();
+    } else if (chr === stringQuote) {
+      break;
+    }
+  }
+}
+
+function parseSpecialExp(chr) {
+  var inExp = 0;
+  var startChr = chr;
+
+  while (!eof()) {
+    chr = peek();
+    if (isStringStart(chr)) {
+      parseString();
+      continue;
+    }
+
+    if (startChr === chr) {
+      inExp++;
+    }
+    if (isExpEnd(startChr, chr)) {
+      inExp--;
+    }
+
+    next();
+
+    if (inExp === 0) {
+      break;
+    }
+  }
+}
+
+/**
+ * syntax:
+ * expression | filterName  [arg  arg [| filterName arg arg]]
+ */
+
+function parseExpression() {
+  var start = index;
+  while (!eof()) {
+    chr = peek();
+    if (isStringStart(chr)) {
+      parseString();
+    } else if (isExpStart(chr)) {
+      parseSpecialExp(chr);
+    } else if (chr === pipeChr) {
+      next();
+      chr = peek();
+      if (chr === pipeChr) {
+        next();
+      } else {
+        if (state === startState || state === filterArgState) {
+          state = filterState;
+        }
+        break;
+      }
+    } else if (chr === spaceChr && (state === filterNameState || state === filterArgState)) {
+      eatSpace();
+      break;
+    } else {
+      if (state === filterState) {
+        state = filterNameState;
+      }
+      next();
+    }
+  }
+
+  return str.slice(start + 1, index) || null;
+}
+
+function parseFilterList() {
+  var filters = [];
+  while (!eof()) {
+    filters.push(parseFilter());
+  }
+  return filters;
+}
+
+function parseFilter() {
+  var filter = {};
+  var args;
+
+  state = filterState;
+  filter.name = parseExpression().trim();
+
+  state = filterArgState;
+  args = parseFilterArguments();
+
+  if (args.length) {
+    filter.args = args;
+  }
+  return filter;
+}
+
+function parseFilterArguments() {
+  var args = [];
+  while (!eof() && state !== filterState) {
+    var arg = parseExpression();
+    if (!arg) {
+      break;
+    }
+    args.push(processFilterArg(arg));
+  }
+
+  return args;
+}
+
+/**
+ * Check if an argument is dynamic and strip quotes.
+ *
+ * @param {String} arg
+ * @return {Object}
+ */
+
+function processFilterArg(arg) {
+  if (reservedArgRE.test(arg)) {
+    return {
+      value: toNumber(arg),
+      dynamic: false
+    };
+  } else {
+    var stripped = stripQuotes(arg);
+    var dynamic = stripped === arg;
+    return {
+      value: dynamic ? arg : stripped,
+      dynamic: dynamic
+    };
+  }
+}
+
+/**
+ * Parse a directive value and extract the expression
+ * and its filters into a descriptor.
+ *
+ * Example:
+ *
+ * "a + 1 | uppercase" will yield:
+ * {
+ *   expression: 'a + 1',
+ *   filters: [
+ *     { name: 'uppercase', args: null }
+ *   ]
+ * }
+ *
+ * @param {String} s
+ * @return {Object}
+ */
+
+function parseDirective(s) {
+  var hit = cache$1.get(s);
+  if (hit) {
+    return hit;
+  }
+
+  // reset parser state
+  str = s;
+  dir = {};
+  len = str.length;
+  index = -1;
+  chr = '';
+  state = startState;
+
+  var filters;
+
+  if (str.indexOf('|') < 0) {
+    dir.expression = str.trim();
+  } else {
+    dir.expression = parseExpression().trim();
+    filters = parseFilterList();
+    if (filters.length) {
+      dir.filters = filters;
+    }
+  }
+
+  cache$1.put(s, dir);
+  return dir;
+}
+
+var directive = Object.freeze({
+  parseDirective: parseDirective
+});
+
+var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g;
+var cache = undefined;
+var tagRE = undefined;
+var htmlRE = undefined;
+/**
+ * Escape a string so it can be used in a RegExp
+ * constructor.
+ *
+ * @param {String} str
+ */
+
+function escapeRegex(str) {
+  return str.replace(regexEscapeRE, '\\$&');
+}
+
+function compileRegex() {
+  var open = escapeRegex(config.delimiters[0]);
+  var close = escapeRegex(config.delimiters[1]);
+  var unsafeOpen = escapeRegex(config.unsafeDelimiters[0]);
+  var unsafeClose = escapeRegex(config.unsafeDelimiters[1]);
+  tagRE = new RegExp(unsafeOpen + '((?:.|\\n)+?)' + unsafeClose + '|' + open + '((?:.|\\n)+?)' + close, 'g');
+  htmlRE = new RegExp('^' + unsafeOpen + '((?:.|\\n)+?)' + unsafeClose + '$');
+  // reset cache
+  cache = new Cache(1000);
+}
+
+/**
+ * Parse a template text string into an array of tokens.
+ *
+ * @param {String} text
+ * @return {Array<Object> | null}
+ *               - {String} type
+ *               - {String} value
+ *               - {Boolean} [html]
+ *               - {Boolean} [oneTime]
+ */
+
+function parseText(text) {
+  if (!cache) {
+    compileRegex();
+  }
+  var hit = cache.get(text);
+  if (hit) {
+    return hit;
+  }
+  if (!tagRE.test(text)) {
+    return null;
+  }
+  var tokens = [];
+  var lastIndex = tagRE.lastIndex = 0;
+  var match, index, html, value, first, oneTime;
+  /* eslint-disable no-cond-assign */
+  while (match = tagRE.exec(text)) {
+    /* eslint-enable no-cond-assign */
+    index = match.index;
+    // push text token
+    if (index > lastIndex) {
+      tokens.push({
+        value: text.slice(lastIndex, index)
+      });
+    }
+    // tag token
+    html = htmlRE.test(match[0]);
+    value = html ? match[1] : match[2];
+    first = value.charCodeAt(0);
+    oneTime = first === 42; // *
+    value = oneTime ? value.slice(1) : value;
+    tokens.push({
+      tag: true,
+      value: value.trim(),
+      html: html,
+      oneTime: oneTime
+    });
+    lastIndex = index + match[0].length;
+  }
+  if (lastIndex < text.length) {
+    tokens.push({
+      value: text.slice(lastIndex)
+    });
+  }
+  cache.put(text, tokens);
+  return tokens;
+}
+
+/**
+ * Format a list of tokens into an expression.
+ * e.g. tokens parsed from 'a {{b}} c' can be serialized
+ * into one single expression as '"a " + b + " c"'.
+ *
+ * @param {Array} tokens
+ * @param {Vue} [vm]
+ * @return {String}
+ */
+
+function tokensToExp(tokens, vm) {
+  if (tokens.length > 1) {
+    return tokens.map(function (token) {
+      return formatToken(token, vm);
+    }).join('+');
+  } else {
+    return formatToken(tokens[0], vm, true);
+  }
+}
+
+/**
+ * Format a single token.
+ *
+ * @param {Object} token
+ * @param {Vue} [vm]
+ * @param {Boolean} [single]
+ * @return {String}
+ */
+
+function formatToken(token, vm, single) {
+  return token.tag ? token.oneTime && vm ? '"' + vm.$eval(token.value) + '"' : inlineFilters(token.value, single) : '"' + token.value + '"';
+}
+
+/**
+ * For an attribute with multiple interpolation tags,
+ * e.g. attr="some-{{thing | filter}}", in order to combine
+ * the whole thing into a single watchable expression, we
+ * have to inline those filters. This function does exactly
+ * that. This is a bit hacky but it avoids heavy changes
+ * to directive parser and watcher mechanism.
+ *
+ * @param {String} exp
+ * @param {Boolean} single
+ * @return {String}
+ */
+
+var filterRE = /[^|]\|[^|]/;
+function inlineFilters(exp, single) {
+  if (!filterRE.test(exp)) {
+    return single ? exp : '(' + exp + ')';
+  } else {
+    var dir = parseDirective(exp);
+    if (!dir.filters) {
+      return '(' + exp + ')';
+    } else {
+      return 'this._applyFilters(' + dir.expression + // value
+      ',null,' + // oldValue (null for read)
+      JSON.stringify(dir.filters) + // filter descriptors
+      ',false)'; // write?
+    }
+  }
+}
+
+var text = Object.freeze({
+  compileRegex: compileRegex,
+  parseText: parseText,
+  tokensToExp: tokensToExp
+});
+
+var delimiters = ['{{', '}}'];
+var unsafeDelimiters = ['{{{', '}}}'];
+
+var config = Object.defineProperties({
+
+  /**
+   * Whether to print debug messages.
+   * Also enables stack trace for warnings.
+   *
+   * @type {Boolean}
+   */
+
+  debug: false,
+
+  /**
+   * Whether to suppress warnings.
+   *
+   * @type {Boolean}
+   */
+
+  silent: false,
+
+  /**
+   * Whether to use async rendering.
+   */
+
+  async: true,
+
+  /**
+   * Whether to warn against errors caught when evaluating
+   * expressions.
+   */
+
+  warnExpressionErrors: true,
+
+  /**
+   * Whether to allow devtools inspection.
+   * Disabled by default in production builds.
+   */
+
+  devtools: process.env.NODE_ENV !== 'production',
+
+  /**
+   * Internal flag to indicate the delimiters have been
+   * changed.
+   *
+   * @type {Boolean}
+   */
+
+  _delimitersChanged: true,
+
+  /**
+   * List of asset types that a component can own.
+   *
+   * @type {Array}
+   */
+
+  _assetTypes: ['component', 'directive', 'elementDirective', 'filter', 'transition', 'partial'],
+
+  /**
+   * prop binding modes
+   */
+
+  _propBindingModes: {
+    ONE_WAY: 0,
+    TWO_WAY: 1,
+    ONE_TIME: 2
+  },
+
+  /**
+   * Max circular updates allowed in a batcher flush cycle.
+   */
+
+  _maxUpdateCount: 100
+
+}, {
+  delimiters: { /**
+                 * Interpolation delimiters. Changing these would trigger
+                 * the text parser to re-compile the regular expressions.
+                 *
+                 * @type {Array<String>}
+                 */
+
+    get: function get() {
+      return delimiters;
+    },
+    set: function set(val) {
+      delimiters = val;
+      compileRegex();
+    },
+    configurable: true,
+    enumerable: true
+  },
+  unsafeDelimiters: {
+    get: function get() {
+      return unsafeDelimiters;
+    },
+    set: function set(val) {
+      unsafeDelimiters = val;
+      compileRegex();
+    },
+    configurable: true,
+    enumerable: true
+  }
+});
+
+var warn = undefined;
+var formatComponentName = undefined;
+
+if (process.env.NODE_ENV !== 'production') {
+  (function () {
+    var hasConsole = typeof console !== 'undefined';
+
+    warn = function (msg, vm) {
+      if (hasConsole && !config.silent) {
+        console.error('[Vue warn]: ' + msg + (vm ? formatComponentName(vm) : ''));
+      }
+    };
+
+    formatComponentName = function (vm) {
+      var name = vm._isVue ? vm.$options.name : vm.name;
+      return name ? ' (found in component: <' + hyphenate(name) + '>)' : '';
+    };
+  })();
+}
+
+/**
+ * Append with transition.
+ *
+ * @param {Element} el
+ * @param {Element} target
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+function appendWithTransition(el, target, vm, cb) {
+  applyTransition(el, 1, function () {
+    target.appendChild(el);
+  }, vm, cb);
+}
+
+/**
+ * InsertBefore with transition.
+ *
+ * @param {Element} el
+ * @param {Element} target
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+function beforeWithTransition(el, target, vm, cb) {
+  applyTransition(el, 1, function () {
+    before(el, target);
+  }, vm, cb);
+}
+
+/**
+ * Remove with transition.
+ *
+ * @param {Element} el
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+function removeWithTransition(el, vm, cb) {
+  applyTransition(el, -1, function () {
+    remove(el);
+  }, vm, cb);
+}
+
+/**
+ * Apply transitions with an operation callback.
+ *
+ * @param {Element} el
+ * @param {Number} direction
+ *                  1: enter
+ *                 -1: leave
+ * @param {Function} op - the actual DOM operation
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+function applyTransition(el, direction, op, vm, cb) {
+  var transition = el.__v_trans;
+  if (!transition ||
+  // skip if there are no js hooks and CSS transition is
+  // not supported
+  !transition.hooks && !transitionEndEvent ||
+  // skip transitions for initial compile
+  !vm._isCompiled ||
+  // if the vm is being manipulated by a parent directive
+  // during the parent's compilation phase, skip the
+  // animation.
+  vm.$parent && !vm.$parent._isCompiled) {
+    op();
+    if (cb) cb();
+    return;
+  }
+  var action = direction > 0 ? 'enter' : 'leave';
+  transition[action](op, cb);
+}
+
+var transition = Object.freeze({
+  appendWithTransition: appendWithTransition,
+  beforeWithTransition: beforeWithTransition,
+  removeWithTransition: removeWithTransition,
+  applyTransition: applyTransition
+});
+
+/**
+ * Query an element selector if it's not an element already.
+ *
+ * @param {String|Element} el
+ * @return {Element}
+ */
+
+function query(el) {
+  if (typeof el === 'string') {
+    var selector = el;
+    el = document.querySelector(el);
+    if (!el) {
+      process.env.NODE_ENV !== 'production' && warn('Cannot find element: ' + selector);
+    }
+  }
+  return el;
+}
+
+/**
+ * Check if a node is in the document.
+ * Note: document.documentElement.contains should work here
+ * but always returns false for comment nodes in phantomjs,
+ * making unit tests difficult. This is fixed by doing the
+ * contains() check on the node's parentNode instead of
+ * the node itself.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+function inDoc(node) {
+  if (!node) return false;
+  var doc = node.ownerDocument.documentElement;
+  var parent = node.parentNode;
+  return doc === node || doc === parent || !!(parent && parent.nodeType === 1 && doc.contains(parent));
+}
+
+/**
+ * Get and remove an attribute from a node.
+ *
+ * @param {Node} node
+ * @param {String} _attr
+ */
+
+function getAttr(node, _attr) {
+  var val = node.getAttribute(_attr);
+  if (val !== null) {
+    node.removeAttribute(_attr);
+  }
+  return val;
+}
+
+/**
+ * Get an attribute with colon or v-bind: prefix.
+ *
+ * @param {Node} node
+ * @param {String} name
+ * @return {String|null}
+ */
+
+function getBindAttr(node, name) {
+  var val = getAttr(node, ':' + name);
+  if (val === null) {
+    val = getAttr(node, 'v-bind:' + name);
+  }
+  return val;
+}
+
+/**
+ * Check the presence of a bind attribute.
+ *
+ * @param {Node} node
+ * @param {String} name
+ * @return {Boolean}
+ */
+
+function hasBindAttr(node, name) {
+  return node.hasAttribute(name) || node.hasAttribute(':' + name) || node.hasAttribute('v-bind:' + name);
+}
+
+/**
+ * Insert el before target
+ *
+ * @param {Element} el
+ * @param {Element} target
+ */
+
+function before(el, target) {
+  target.parentNode.insertBefore(el, target);
+}
+
+/**
+ * Insert el after target
+ *
+ * @param {Element} el
+ * @param {Element} target
+ */
+
+function after(el, target) {
+  if (target.nextSibling) {
+    before(el, target.nextSibling);
+  } else {
+    target.parentNode.appendChild(el);
+  }
+}
+
+/**
+ * Remove el from DOM
+ *
+ * @param {Element} el
+ */
+
+function remove(el) {
+  el.parentNode.removeChild(el);
+}
+
+/**
+ * Prepend el to target
+ *
+ * @param {Element} el
+ * @param {Element} target
+ */
+
+function prepend(el, target) {
+  if (target.firstChild) {
+    before(el, target.firstChild);
+  } else {
+    target.appendChild(el);
+  }
+}
+
+/**
+ * Replace target with el
+ *
+ * @param {Element} target
+ * @param {Element} el
+ */
+
+function replace(target, el) {
+  var parent = target.parentNode;
+  if (parent) {
+    parent.replaceChild(el, target);
+  }
+}
+
+/**
+ * Add event listener shorthand.
+ *
+ * @param {Element} el
+ * @param {String} event
+ * @param {Function} cb
+ * @param {Boolean} [useCapture]
+ */
+
+function on(el, event, cb, useCapture) {
+  el.addEventListener(event, cb, useCapture);
+}
+
+/**
+ * Remove event listener shorthand.
+ *
+ * @param {Element} el
+ * @param {String} event
+ * @param {Function} cb
+ */
+
+function off(el, event, cb) {
+  el.removeEventListener(event, cb);
+}
+
+/**
+ * For IE9 compat: when both class and :class are present
+ * getAttribute('class') returns wrong value...
+ *
+ * @param {Element} el
+ * @return {String}
+ */
+
+function getClass(el) {
+  var classname = el.className;
+  if (typeof classname === 'object') {
+    classname = classname.baseVal || '';
+  }
+  return classname;
+}
+
+/**
+ * In IE9, setAttribute('class') will result in empty class
+ * if the element also has the :class attribute; However in
+ * PhantomJS, setting `className` does not work on SVG elements...
+ * So we have to do a conditional check here.
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+function setClass(el, cls) {
+  /* istanbul ignore if */
+  if (isIE9 && !/svg$/.test(el.namespaceURI)) {
+    el.className = cls;
+  } else {
+    el.setAttribute('class', cls);
+  }
+}
+
+/**
+ * Add class with compatibility for IE & SVG
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+function addClass(el, cls) {
+  if (el.classList) {
+    el.classList.add(cls);
+  } else {
+    var cur = ' ' + getClass(el) + ' ';
+    if (cur.indexOf(' ' + cls + ' ') < 0) {
+      setClass(el, (cur + cls).trim());
+    }
+  }
+}
+
+/**
+ * Remove class with compatibility for IE & SVG
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+function removeClass(el, cls) {
+  if (el.classList) {
+    el.classList.remove(cls);
+  } else {
+    var cur = ' ' + getClass(el) + ' ';
+    var tar = ' ' + cls + ' ';
+    while (cur.indexOf(tar) >= 0) {
+      cur = cur.replace(tar, ' ');
+    }
+    setClass(el, cur.trim());
+  }
+  if (!el.className) {
+    el.removeAttribute('class');
+  }
+}
+
+/**
+ * Extract raw content inside an element into a temporary
+ * container div
+ *
+ * @param {Element} el
+ * @param {Boolean} asFragment
+ * @return {Element|DocumentFragment}
+ */
+
+function extractContent(el, asFragment) {
+  var child;
+  var rawContent;
+  /* istanbul ignore if */
+  if (isTemplate(el) && isFragment(el.content)) {
+    el = el.content;
+  }
+  if (el.hasChildNodes()) {
+    trimNode(el);
+    rawContent = asFragment ? document.createDocumentFragment() : document.createElement('div');
+    /* eslint-disable no-cond-assign */
+    while (child = el.firstChild) {
+      /* eslint-enable no-cond-assign */
+      rawContent.appendChild(child);
+    }
+  }
+  return rawContent;
+}
+
+/**
+ * Trim possible empty head/tail text and comment
+ * nodes inside a parent.
+ *
+ * @param {Node} node
+ */
+
+function trimNode(node) {
+  var child;
+  /* eslint-disable no-sequences */
+  while ((child = node.firstChild, isTrimmable(child))) {
+    node.removeChild(child);
+  }
+  while ((child = node.lastChild, isTrimmable(child))) {
+    node.removeChild(child);
+  }
+  /* eslint-enable no-sequences */
+}
+
+function isTrimmable(node) {
+  return node && (node.nodeType === 3 && !node.data.trim() || node.nodeType === 8);
+}
+
+/**
+ * Check if an element is a template tag.
+ * Note if the template appears inside an SVG its tagName
+ * will be in lowercase.
+ *
+ * @param {Element} el
+ */
+
+function isTemplate(el) {
+  return el.tagName && el.tagName.toLowerCase() === 'template';
+}
+
+/**
+ * Create an "anchor" for performing dom insertion/removals.
+ * This is used in a number of scenarios:
+ * - fragment instance
+ * - v-html
+ * - v-if
+ * - v-for
+ * - component
+ *
+ * @param {String} content
+ * @param {Boolean} persist - IE trashes empty textNodes on
+ *                            cloneNode(true), so in certain
+ *                            cases the anchor needs to be
+ *                            non-empty to be persisted in
+ *                            templates.
+ * @return {Comment|Text}
+ */
+
+function createAnchor(content, persist) {
+  var anchor = config.debug ? document.createComment(content) : document.createTextNode(persist ? ' ' : '');
+  anchor.__v_anchor = true;
+  return anchor;
+}
+
+/**
+ * Find a component ref attribute that starts with $.
+ *
+ * @param {Element} node
+ * @return {String|undefined}
+ */
+
+var refRE = /^v-ref:/;
+
+function findRef(node) {
+  if (node.hasAttributes()) {
+    var attrs = node.attributes;
+    for (var i = 0, l = attrs.length; i < l; i++) {
+      var name = attrs[i].name;
+      if (refRE.test(name)) {
+        return camelize(name.replace(refRE, ''));
+      }
+    }
+  }
+}
+
+/**
+ * Map a function to a range of nodes .
+ *
+ * @param {Node} node
+ * @param {Node} end
+ * @param {Function} op
+ */
+
+function mapNodeRange(node, end, op) {
+  var next;
+  while (node !== end) {
+    next = node.nextSibling;
+    op(node);
+    node = next;
+  }
+  op(end);
+}
+
+/**
+ * Remove a range of nodes with transition, store
+ * the nodes in a fragment with correct ordering,
+ * and call callback when done.
+ *
+ * @param {Node} start
+ * @param {Node} end
+ * @param {Vue} vm
+ * @param {DocumentFragment} frag
+ * @param {Function} cb
+ */
+
+function removeNodeRange(start, end, vm, frag, cb) {
+  var done = false;
+  var removed = 0;
+  var nodes = [];
+  mapNodeRange(start, end, function (node) {
+    if (node === end) done = true;
+    nodes.push(node);
+    removeWithTransition(node, vm, onRemoved);
+  });
+  function onRemoved() {
+    removed++;
+    if (done && removed >= nodes.length) {
+      for (var i = 0; i < nodes.length; i++) {
+        frag.appendChild(nodes[i]);
+      }
+      cb && cb();
+    }
+  }
+}
+
+/**
+ * Check if a node is a DocumentFragment.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+function isFragment(node) {
+  return node && node.nodeType === 11;
+}
+
+/**
+ * Get outerHTML of elements, taking care
+ * of SVG elements in IE as well.
+ *
+ * @param {Element} el
+ * @return {String}
+ */
+
+function getOuterHTML(el) {
+  if (el.outerHTML) {
+    return el.outerHTML;
+  } else {
+    var container = document.createElement('div');
+    container.appendChild(el.cloneNode(true));
+    return container.innerHTML;
+  }
+}
+
+var commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/i;
+var reservedTagRE = /^(slot|partial|component)$/i;
+
+var isUnknownElement = undefined;
+if (process.env.NODE_ENV !== 'production') {
+  isUnknownElement = function (el, tag) {
+    if (tag.indexOf('-') > -1) {
+      // http://stackoverflow.com/a/28210364/1070244
+      return el.constructor === window.HTMLUnknownElement || el.constructor === window.HTMLElement;
+    } else {
+      return (/HTMLUnknownElement/.test(el.toString()) &&
+        // Chrome returns unknown for several HTML5 elements.
+        // https://code.google.com/p/chromium/issues/detail?id=540526
+        // Firefox returns unknown for some "Interactive elements."
+        !/^(data|time|rtc|rb|details|dialog|summary)$/.test(tag)
+      );
+    }
+  };
+}
+
+/**
+ * Check if an element is a component, if yes return its
+ * component id.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Object|undefined}
+ */
+
+function checkComponentAttr(el, options) {
+  var tag = el.tagName.toLowerCase();
+  var hasAttrs = el.hasAttributes();
+  if (!commonTagRE.test(tag) && !reservedTagRE.test(tag)) {
+    if (resolveAsset(options, 'components', tag)) {
+      return { id: tag };
+    } else {
+      var is = hasAttrs && getIsBinding(el, options);
+      if (is) {
+        return is;
+      } else if (process.env.NODE_ENV !== 'production') {
+        var expectedTag = options._componentNameMap && options._componentNameMap[tag];
+        if (expectedTag) {
+          warn('Unknown custom element: <' + tag + '> - ' + 'did you mean <' + expectedTag + '>? ' + 'HTML is case-insensitive, remember to use kebab-case in templates.');
+        } else if (isUnknownElement(el, tag)) {
+          warn('Unknown custom element: <' + tag + '> - did you ' + 'register the component correctly? For recursive components, ' + 'make sure to provide the "name" option.');
+        }
+      }
+    }
+  } else if (hasAttrs) {
+    return getIsBinding(el, options);
+  }
+}
+
+/**
+ * Get "is" binding from an element.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Object|undefined}
+ */
+
+function getIsBinding(el, options) {
+  // dynamic syntax
+  var exp = el.getAttribute('is');
+  if (exp != null) {
+    if (resolveAsset(options, 'components', exp)) {
+      el.removeAttribute('is');
+      return { id: exp };
+    }
+  } else {
+    exp = getBindAttr(el, 'is');
+    if (exp != null) {
+      return { id: exp, dynamic: true };
+    }
+  }
+}
+
+/**
+ * Option overwriting strategies are functions that handle
+ * how to merge a parent option value and a child option
+ * value into the final value.
+ *
+ * All strategy functions follow the same signature:
+ *
+ * @param {*} parentVal
+ * @param {*} childVal
+ * @param {Vue} [vm]
+ */
+
+var strats = config.optionMergeStrategies = Object.create(null);
+
+/**
+ * Helper that recursively merges two data objects together.
+ */
+
+function mergeData(to, from) {
+  var key, toVal, fromVal;
+  for (key in from) {
+    toVal = to[key];
+    fromVal = from[key];
+    if (!hasOwn(to, key)) {
+      set(to, key, fromVal);
+    } else if (isObject(toVal) && isObject(fromVal)) {
+      mergeData(toVal, fromVal);
+    }
+  }
+  return to;
+}
+
+/**
+ * Data
+ */
+
+strats.data = function (parentVal, childVal, vm) {
+  if (!vm) {
+    // in a Vue.extend merge, both should be functions
+    if (!childVal) {
+      return parentVal;
+    }
+    if (typeof childVal !== 'function') {
+      process.env.NODE_ENV !== 'production' && warn('The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm);
+      return parentVal;
+    }
+    if (!parentVal) {
+      return childVal;
+    }
+    // when parentVal & childVal are both present,
+    // we need to return a function that returns the
+    // merged result of both functions... no need to
+    // check if parentVal is a function here because
+    // it has to be a function to pass previous merges.
+    return function mergedDataFn() {
+      return mergeData(childVal.call(this), parentVal.call(this));
+    };
+  } else if (parentVal || childVal) {
+    return function mergedInstanceDataFn() {
+      // instance merge
+      var instanceData = typeof childVal === 'function' ? childVal.call(vm) : childVal;
+      var defaultData = typeof parentVal === 'function' ? parentVal.call(vm) : undefined;
+      if (instanceData) {
+        return mergeData(instanceData, defaultData);
+      } else {
+        return defaultData;
+      }
+    };
+  }
+};
+
+/**
+ * El
+ */
+
+strats.el = function (parentVal, childVal, vm) {
+  if (!vm && childVal && typeof childVal !== 'function') {
+    process.env.NODE_ENV !== 'production' && warn('The "el" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm);
+    return;
+  }
+  var ret = childVal || parentVal;
+  // invoke the element factory if this is instance merge
+  return vm && typeof ret === 'function' ? ret.call(vm) : ret;
+};
+
+/**
+ * Hooks and param attributes are merged as arrays.
+ */
+
+strats.init = strats.created = strats.ready = strats.attached = strats.detached = strats.beforeCompile = strats.compiled = strats.beforeDestroy = strats.destroyed = strats.activate = function (parentVal, childVal) {
+  return childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [childVal] : parentVal;
+};
+
+/**
+ * Assets
+ *
+ * When a vm is present (instance creation), we need to do
+ * a three-way merge between constructor options, instance
+ * options and parent options.
+ */
+
+function mergeAssets(parentVal, childVal) {
+  var res = Object.create(parentVal || null);
+  return childVal ? extend(res, guardArrayAssets(childVal)) : res;
+}
+
+config._assetTypes.forEach(function (type) {
+  strats[type + 's'] = mergeAssets;
+});
+
+/**
+ * Events & Watchers.
+ *
+ * Events & watchers hashes should not overwrite one
+ * another, so we merge them as arrays.
+ */
+
+strats.watch = strats.events = function (parentVal, childVal) {
+  if (!childVal) return parentVal;
+  if (!parentVal) return childVal;
+  var ret = {};
+  extend(ret, parentVal);
+  for (var key in childVal) {
+    var parent = ret[key];
+    var child = childVal[key];
+    if (parent && !isArray(parent)) {
+      parent = [parent];
+    }
+    ret[key] = parent ? parent.concat(child) : [child];
+  }
+  return ret;
+};
+
+/**
+ * Other object hashes.
+ */
+
+strats.props = strats.methods = strats.computed = function (parentVal, childVal) {
+  if (!childVal) return parentVal;
+  if (!parentVal) return childVal;
+  var ret = Object.create(null);
+  extend(ret, parentVal);
+  extend(ret, childVal);
+  return ret;
+};
+
+/**
+ * Default strategy.
+ */
+
+var defaultStrat = function defaultStrat(parentVal, childVal) {
+  return childVal === undefined ? parentVal : childVal;
+};
+
+/**
+ * Make sure component options get converted to actual
+ * constructors.
+ *
+ * @param {Object} options
+ */
+
+function guardComponents(options) {
+  if (options.components) {
+    var components = options.components = guardArrayAssets(options.components);
+    var ids = Object.keys(components);
+    var def;
+    if (process.env.NODE_ENV !== 'production') {
+      var map = options._componentNameMap = {};
+    }
+    for (var i = 0, l = ids.length; i < l; i++) {
+      var key = ids[i];
+      if (commonTagRE.test(key) || reservedTagRE.test(key)) {
+        process.env.NODE_ENV !== 'production' && warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + key);
+        continue;
+      }
+      // record a all lowercase <-> kebab-case mapping for
+      // possible custom element case error warning
+      if (process.env.NODE_ENV !== 'production') {
+        map[key.replace(/-/g, '').toLowerCase()] = hyphenate(key);
+      }
+      def = components[key];
+      if (isPlainObject(def)) {
+        components[key] = Vue.extend(def);
+      }
+    }
+  }
+}
+
+/**
+ * Ensure all props option syntax are normalized into the
+ * Object-based format.
+ *
+ * @param {Object} options
+ */
+
+function guardProps(options) {
+  var props = options.props;
+  var i, val;
+  if (isArray(props)) {
+    options.props = {};
+    i = props.length;
+    while (i--) {
+      val = props[i];
+      if (typeof val === 'string') {
+        options.props[val] = null;
+      } else if (val.name) {
+        options.props[val.name] = val;
+      }
+    }
+  } else if (isPlainObject(props)) {
+    var keys = Object.keys(props);
+    i = keys.length;
+    while (i--) {
+      val = props[keys[i]];
+      if (typeof val === 'function') {
+        props[keys[i]] = { type: val };
+      }
+    }
+  }
+}
+
+/**
+ * Guard an Array-format assets option and converted it
+ * into the key-value Object format.
+ *
+ * @param {Object|Array} assets
+ * @return {Object}
+ */
+
+function guardArrayAssets(assets) {
+  if (isArray(assets)) {
+    var res = {};
+    var i = assets.length;
+    var asset;
+    while (i--) {
+      asset = assets[i];
+      var id = typeof asset === 'function' ? asset.options && asset.options.name || asset.id : asset.name || asset.id;
+      if (!id) {
+        process.env.NODE_ENV !== 'production' && warn('Array-syntax assets must provide a "name" or "id" field.');
+      } else {
+        res[id] = asset;
+      }
+    }
+    return res;
+  }
+  return assets;
+}
+
+/**
+ * Merge two option objects into a new one.
+ * Core utility used in both instantiation and inheritance.
+ *
+ * @param {Object} parent
+ * @param {Object} child
+ * @param {Vue} [vm] - if vm is present, indicates this is
+ *                     an instantiation merge.
+ */
+
+function mergeOptions(parent, child, vm) {
+  guardComponents(child);
+  guardProps(child);
+  if (process.env.NODE_ENV !== 'production') {
+    if (child.propsData && !vm) {
+      warn('propsData can only be used as an instantiation option.');
+    }
+  }
+  var options = {};
+  var key;
+  if (child['extends']) {
+    parent = typeof child['extends'] === 'function' ? mergeOptions(parent, child['extends'].options, vm) : mergeOptions(parent, child['extends'], vm);
+  }
+  if (child.mixins) {
+    for (var i = 0, l = child.mixins.length; i < l; i++) {
+      var mixin = child.mixins[i];
+      var mixinOptions = mixin.prototype instanceof Vue ? mixin.options : mixin;
+      parent = mergeOptions(parent, mixinOptions, vm);
+    }
+  }
+  for (key in parent) {
+    mergeField(key);
+  }
+  for (key in child) {
+    if (!hasOwn(parent, key)) {
+      mergeField(key);
+    }
+  }
+  function mergeField(key) {
+    var strat = strats[key] || defaultStrat;
+    options[key] = strat(parent[key], child[key], vm, key);
+  }
+  return options;
+}
+
+/**
+ * Resolve an asset.
+ * This function is used because child instances need access
+ * to assets defined in its ancestor chain.
+ *
+ * @param {Object} options
+ * @param {String} type
+ * @param {String} id
+ * @param {Boolean} warnMissing
+ * @return {Object|Function}
+ */
+
+function resolveAsset(options, type, id, warnMissing) {
+  /* istanbul ignore if */
+  if (typeof id !== 'string') {
+    return;
+  }
+  var assets = options[type];
+  var camelizedId;
+  var res = assets[id] ||
+  // camelCase ID
+  assets[camelizedId = camelize(id)] ||
+  // Pascal Case ID
+  assets[camelizedId.charAt(0).toUpperCase() + camelizedId.slice(1)];
+  if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
+    warn('Failed to resolve ' + type.slice(0, -1) + ': ' + id, options);
+  }
+  return res;
+}
+
+var uid$1 = 0;
+
+/**
+ * A dep is an observable that can have multiple
+ * directives subscribing to it.
+ *
+ * @constructor
+ */
+function Dep() {
+  this.id = uid$1++;
+  this.subs = [];
+}
+
+// the current target watcher being evaluated.
+// this is globally unique because there could be only one
+// watcher being evaluated at any time.
+Dep.target = null;
+
+/**
+ * Add a directive subscriber.
+ *
+ * @param {Directive} sub
+ */
+
+Dep.prototype.addSub = function (sub) {
+  this.subs.push(sub);
+};
+
+/**
+ * Remove a directive subscriber.
+ *
+ * @param {Directive} sub
+ */
+
+Dep.prototype.removeSub = function (sub) {
+  this.subs.$remove(sub);
+};
+
+/**
+ * Add self as a dependency to the target watcher.
+ */
+
+Dep.prototype.depend = function () {
+  Dep.target.addDep(this);
+};
+
+/**
+ * Notify all subscribers of a new value.
+ */
+
+Dep.prototype.notify = function () {
+  // stablize the subscriber list first
+  var subs = toArray(this.subs);
+  for (var i = 0, l = subs.length; i < l; i++) {
+    subs[i].update();
+  }
+};
+
+var arrayProto = Array.prototype;
+var arrayMethods = Object.create(arrayProto)
+
+/**
+ * Intercept mutating methods and emit events
+ */
+
+;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {
+  // cache original method
+  var original = arrayProto[method];
+  def(arrayMethods, method, function mutator() {
+    // avoid leaking arguments:
+    // http://jsperf.com/closure-with-arguments
+    var i = arguments.length;
+    var args = new Array(i);
+    while (i--) {
+      args[i] = arguments[i];
+    }
+    var result = original.apply(this, args);
+    var ob = this.__ob__;
+    var inserted;
+    switch (method) {
+      case 'push':
+        inserted = args;
+        break;
+      case 'unshift':
+        inserted = args;
+        break;
+      case 'splice':
+        inserted = args.slice(2);
+        break;
+    }
+    if (inserted) ob.observeArray(inserted);
+    // notify change
+    ob.dep.notify();
+    return result;
+  });
+});
+
+/**
+ * Swap the element at the given index with a new value
+ * and emits corresponding event.
+ *
+ * @param {Number} index
+ * @param {*} val
+ * @return {*} - replaced element
+ */
+
+def(arrayProto, '$set', function $set(index, val) {
+  if (index >= this.length) {
+    this.length = Number(index) + 1;
+  }
+  return this.splice(index, 1, val)[0];
+});
+
+/**
+ * Convenience method to remove the element at given index or target element reference.
+ *
+ * @param {*} item
+ */
+
+def(arrayProto, '$remove', function $remove(item) {
+  /* istanbul ignore if */
+  if (!this.length) return;
+  var index = indexOf(this, item);
+  if (index > -1) {
+    return this.splice(index, 1);
+  }
+});
+
+var arrayKeys = Object.getOwnPropertyNames(arrayMethods);
+
+/**
+ * By default, when a reactive property is set, the new value is
+ * also converted to become reactive. However in certain cases, e.g.
+ * v-for scope alias and props, we don't want to force conversion
+ * because the value may be a nested value under a frozen data structure.
+ *
+ * So whenever we want to set a reactive property without forcing
+ * conversion on the new value, we wrap that call inside this function.
+ */
+
+var shouldConvert = true;
+
+function withoutConversion(fn) {
+  shouldConvert = false;
+  fn();
+  shouldConvert = true;
+}
+
+/**
+ * Observer class that are attached to each observed
+ * object. Once attached, the observer converts target
+ * object's property keys into getter/setters that
+ * collect dependencies and dispatches updates.
+ *
+ * @param {Array|Object} value
+ * @constructor
+ */
+
+function Observer(value) {
+  this.value = value;
+  this.dep = new Dep();
+  def(value, '__ob__', this);
+  if (isArray(value)) {
+    var augment = hasProto ? protoAugment : copyAugment;
+    augment(value, arrayMethods, arrayKeys);
+    this.observeArray(value);
+  } else {
+    this.walk(value);
+  }
+}
+
+// Instance methods
+
+/**
+ * Walk through each property and convert them into
+ * getter/setters. This method should only be called when
+ * value type is Object.
+ *
+ * @param {Object} obj
+ */
+
+Observer.prototype.walk = function (obj) {
+  var keys = Object.keys(obj);
+  for (var i = 0, l = keys.length; i < l; i++) {
+    this.convert(keys[i], obj[keys[i]]);
+  }
+};
+
+/**
+ * Observe a list of Array items.
+ *
+ * @param {Array} items
+ */
+
+Observer.prototype.observeArray = function (items) {
+  for (var i = 0, l = items.length; i < l; i++) {
+    observe(items[i]);
+  }
+};
+
+/**
+ * Convert a property into getter/setter so we can emit
+ * the events when the property is accessed/changed.
+ *
+ * @param {String} key
+ * @param {*} val
+ */
+
+Observer.prototype.convert = function (key, val) {
+  defineReactive(this.value, key, val);
+};
+
+/**
+ * Add an owner vm, so that when $set/$delete mutations
+ * happen we can notify owner vms to proxy the keys and
+ * digest the watchers. This is only called when the object
+ * is observed as an instance's root $data.
+ *
+ * @param {Vue} vm
+ */
+
+Observer.prototype.addVm = function (vm) {
+  (this.vms || (this.vms = [])).push(vm);
+};
+
+/**
+ * Remove an owner vm. This is called when the object is
+ * swapped out as an instance's $data object.
+ *
+ * @param {Vue} vm
+ */
+
+Observer.prototype.removeVm = function (vm) {
+  this.vms.$remove(vm);
+};
+
+// helpers
+
+/**
+ * Augment an target Object or Array by intercepting
+ * the prototype chain using __proto__
+ *
+ * @param {Object|Array} target
+ * @param {Object} src
+ */
+
+function protoAugment(target, src) {
+  /* eslint-disable no-proto */
+  target.__proto__ = src;
+  /* eslint-enable no-proto */
+}
+
+/**
+ * Augment an target Object or Array by defining
+ * hidden properties.
+ *
+ * @param {Object|Array} target
+ * @param {Object} proto
+ */
+
+function copyAugment(target, src, keys) {
+  for (var i = 0, l = keys.length; i < l; i++) {
+    var key = keys[i];
+    def(target, key, src[key]);
+  }
+}
+
+/**
+ * Attempt to create an observer instance for a value,
+ * returns the new observer if successfully observed,
+ * or the existing observer if the value already has one.
+ *
+ * @param {*} value
+ * @param {Vue} [vm]
+ * @return {Observer|undefined}
+ * @static
+ */
+
+function observe(value, vm) {
+  if (!value || typeof value !== 'object') {
+    return;
+  }
+  var ob;
+  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
+    ob = value.__ob__;
+  } else if (shouldConvert && (isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue) {
+    ob = new Observer(value);
+  }
+  if (ob && vm) {
+    ob.addVm(vm);
+  }
+  return ob;
+}
+
+/**
+ * Define a reactive property on an Object.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {*} val
+ */
+
+function defineReactive(obj, key, val) {
+  var dep = new Dep();
+
+  var property = Object.getOwnPropertyDescriptor(obj, key);
+  if (property && property.configurable === false) {
+    return;
+  }
+
+  // cater for pre-defined getter/setters
+  var getter = property && property.get;
+  var setter = property && property.set;
+
+  var childOb = observe(val);
+  Object.defineProperty(obj, key, {
+    enumerable: true,
+    configurable: true,
+    get: function reactiveGetter() {
+      var value = getter ? getter.call(obj) : val;
+      if (Dep.target) {
+        dep.depend();
+        if (childOb) {
+          childOb.dep.depend();
+        }
+        if (isArray(value)) {
+          for (var e, i = 0, l = value.length; i < l; i++) {
+            e = value[i];
+            e && e.__ob__ && e.__ob__.dep.depend();
+          }
+        }
+      }
+      return value;
+    },
+    set: function reactiveSetter(newVal) {
+      var value = getter ? getter.call(obj) : val;
+      if (newVal === value) {
+        return;
+      }
+      if (setter) {
+        setter.call(obj, newVal);
+      } else {
+        val = newVal;
+      }
+      childOb = observe(newVal);
+      dep.notify();
+    }
+  });
+}
+
+
+
+var util = Object.freeze({
+	defineReactive: defineReactive,
+	set: set,
+	del: del,
+	hasOwn: hasOwn,
+	isLiteral: isLiteral,
+	isReserved: isReserved,
+	_toString: _toString,
+	toNumber: toNumber,
+	toBoolean: toBoolean,
+	stripQuotes: stripQuotes,
+	camelize: camelize,
+	hyphenate: hyphenate,
+	classify: classify,
+	bind: bind,
+	toArray: toArray,
+	extend: extend,
+	isObject: isObject,
+	isPlainObject: isPlainObject,
+	def: def,
+	debounce: _debounce,
+	indexOf: indexOf,
+	cancellable: cancellable,
+	looseEqual: looseEqual,
+	isArray: isArray,
+	hasProto: hasProto,
+	inBrowser: inBrowser,
+	devtools: devtools,
+	isIE: isIE,
+	isIE9: isIE9,
+	isAndroid: isAndroid,
+	isIOS: isIOS,
+	get transitionProp () { return transitionProp; },
+	get transitionEndEvent () { return transitionEndEvent; },
+	get animationProp () { return animationProp; },
+	get animationEndEvent () { return animationEndEvent; },
+	nextTick: nextTick,
+	get _Set () { return _Set; },
+	query: query,
+	inDoc: inDoc,
+	getAttr: getAttr,
+	getBindAttr: getBindAttr,
+	hasBindAttr: hasBindAttr,
+	before: before,
+	after: after,
+	remove: remove,
+	prepend: prepend,
+	replace: replace,
+	on: on,
+	off: off,
+	setClass: setClass,
+	addClass: addClass,
+	removeClass: removeClass,
+	extractContent: extractContent,
+	trimNode: trimNode,
+	isTemplate: isTemplate,
+	createAnchor: createAnchor,
+	findRef: findRef,
+	mapNodeRange: mapNodeRange,
+	removeNodeRange: removeNodeRange,
+	isFragment: isFragment,
+	getOuterHTML: getOuterHTML,
+	mergeOptions: mergeOptions,
+	resolveAsset: resolveAsset,
+	checkComponentAttr: checkComponentAttr,
+	commonTagRE: commonTagRE,
+	reservedTagRE: reservedTagRE,
+	get warn () { return warn; }
+});
+
+var uid = 0;
+
+function initMixin (Vue) {
+  /**
+   * The main init sequence. This is called for every
+   * instance, including ones that are created from extended
+   * constructors.
+   *
+   * @param {Object} options - this options object should be
+   *                           the result of merging class
+   *                           options and the options passed
+   *                           in to the constructor.
+   */
+
+  Vue.prototype._init = function (options) {
+    options = options || {};
+
+    this.$el = null;
+    this.$parent = options.parent;
+    this.$root = this.$parent ? this.$parent.$root : this;
+    this.$children = [];
+    this.$refs = {}; // child vm references
+    this.$els = {}; // element references
+    this._watchers = []; // all watchers as an array
+    this._directives = []; // all directives
+
+    // a uid
+    this._uid = uid++;
+
+    // a flag to avoid this being observed
+    this._isVue = true;
+
+    // events bookkeeping
+    this._events = {}; // registered callbacks
+    this._eventsCount = {}; // for $broadcast optimization
+
+    // fragment instance properties
+    this._isFragment = false;
+    this._fragment = // @type {DocumentFragment}
+    this._fragmentStart = // @type {Text|Comment}
+    this._fragmentEnd = null; // @type {Text|Comment}
+
+    // lifecycle state
+    this._isCompiled = this._isDestroyed = this._isReady = this._isAttached = this._isBeingDestroyed = this._vForRemoving = false;
+    this._unlinkFn = null;
+
+    // context:
+    // if this is a transcluded component, context
+    // will be the common parent vm of this instance
+    // and its host.
+    this._context = options._context || this.$parent;
+
+    // scope:
+    // if this is inside an inline v-for, the scope
+    // will be the intermediate scope created for this
+    // repeat fragment. this is used for linking props
+    // and container directives.
+    this._scope = options._scope;
+
+    // fragment:
+    // if this instance is compiled inside a Fragment, it
+    // needs to register itself as a child of that fragment
+    // for attach/detach to work properly.
+    this._frag = options._frag;
+    if (this._frag) {
+      this._frag.children.push(this);
+    }
+
+    // push self into parent / transclusion host
+    if (this.$parent) {
+      this.$parent.$children.push(this);
+    }
+
+    // merge options.
+    options = this.$options = mergeOptions(this.constructor.options, options, this);
+
+    // set ref
+    this._updateRef();
+
+    // initialize data as empty object.
+    // it will be filled up in _initData().
+    this._data = {};
+
+    // call init hook
+    this._callHook('init');
+
+    // initialize data observation and scope inheritance.
+    this._initState();
+
+    // setup event system and option events.
+    this._initEvents();
+
+    // call created hook
+    this._callHook('created');
+
+    // if `el` option is passed, start compilation.
+    if (options.el) {
+      this.$mount(options.el);
+    }
+  };
+}
+
+var pathCache = new Cache(1000);
+
+// actions
+var APPEND = 0;
+var PUSH = 1;
+var INC_SUB_PATH_DEPTH = 2;
+var PUSH_SUB_PATH = 3;
+
+// states
+var BEFORE_PATH = 0;
+var IN_PATH = 1;
+var BEFORE_IDENT = 2;
+var IN_IDENT = 3;
+var IN_SUB_PATH = 4;
+var IN_SINGLE_QUOTE = 5;
+var IN_DOUBLE_QUOTE = 6;
+var AFTER_PATH = 7;
+var ERROR = 8;
+
+var pathStateMachine = [];
+
+pathStateMachine[BEFORE_PATH] = {
+  'ws': [BEFORE_PATH],
+  'ident': [IN_IDENT, APPEND],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+};
+
+pathStateMachine[IN_PATH] = {
+  'ws': [IN_PATH],
+  '.': [BEFORE_IDENT],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+};
+
+pathStateMachine[BEFORE_IDENT] = {
+  'ws': [BEFORE_IDENT],
+  'ident': [IN_IDENT, APPEND]
+};
+
+pathStateMachine[IN_IDENT] = {
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND],
+  'ws': [IN_PATH, PUSH],
+  '.': [BEFORE_IDENT, PUSH],
+  '[': [IN_SUB_PATH, PUSH],
+  'eof': [AFTER_PATH, PUSH]
+};
+
+pathStateMachine[IN_SUB_PATH] = {
+  "'": [IN_SINGLE_QUOTE, APPEND],
+  '"': [IN_DOUBLE_QUOTE, APPEND],
+  '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
+  ']': [IN_PATH, PUSH_SUB_PATH],
+  'eof': ERROR,
+  'else': [IN_SUB_PATH, APPEND]
+};
+
+pathStateMachine[IN_SINGLE_QUOTE] = {
+  "'": [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_SINGLE_QUOTE, APPEND]
+};
+
+pathStateMachine[IN_DOUBLE_QUOTE] = {
+  '"': [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_DOUBLE_QUOTE, APPEND]
+};
+
+/**
+ * Determine the type of a character in a keypath.
+ *
+ * @param {Char} ch
+ * @return {String} type
+ */
+
+function getPathCharType(ch) {
+  if (ch === undefined) {
+    return 'eof';
+  }
+
+  var code = ch.charCodeAt(0);
+
+  switch (code) {
+    case 0x5B: // [
+    case 0x5D: // ]
+    case 0x2E: // .
+    case 0x22: // "
+    case 0x27: // '
+    case 0x30:
+      // 0
+      return ch;
+
+    case 0x5F: // _
+    case 0x24:
+      // $
+      return 'ident';
+
+    case 0x20: // Space
+    case 0x09: // Tab
+    case 0x0A: // Newline
+    case 0x0D: // Return
+    case 0xA0: // No-break space
+    case 0xFEFF: // Byte Order Mark
+    case 0x2028: // Line Separator
+    case 0x2029:
+      // Paragraph Separator
+      return 'ws';
+  }
+
+  // a-z, A-Z
+  if (code >= 0x61 && code <= 0x7A || code >= 0x41 && code <= 0x5A) {
+    return 'ident';
+  }
+
+  // 1-9
+  if (code >= 0x31 && code <= 0x39) {
+    return 'number';
+  }
+
+  return 'else';
+}
+
+/**
+ * Format a subPath, return its plain form if it is
+ * a literal string or number. Otherwise prepend the
+ * dynamic indicator (*).
+ *
+ * @param {String} path
+ * @return {String}
+ */
+
+function formatSubPath(path) {
+  var trimmed = path.trim();
+  // invalid leading 0
+  if (path.charAt(0) === '0' && isNaN(path)) {
+    return false;
+  }
+  return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed;
+}
+
+/**
+ * Parse a string path into an array of segments
+ *
+ * @param {String} path
+ * @return {Array|undefined}
+ */
+
+function parse(path) {
+  var keys = [];
+  var index = -1;
+  var mode = BEFORE_PATH;
+  var subPathDepth = 0;
+  var c, newChar, key, type, transition, action, typeMap;
+
+  var actions = [];
+
+  actions[PUSH] = function () {
+    if (key !== undefined) {
+      keys.push(key);
+      key = undefined;
+    }
+  };
+
+  actions[APPEND] = function () {
+    if (key === undefined) {
+      key = newChar;
+    } else {
+      key += newChar;
+    }
+  };
+
+  actions[INC_SUB_PATH_DEPTH] = function () {
+    actions[APPEND]();
+    subPathDepth++;
+  };
+
+  actions[PUSH_SUB_PATH] = function () {
+    if (subPathDepth > 0) {
+      subPathDepth--;
+      mode = IN_SUB_PATH;
+      actions[APPEND]();
+    } else {
+      subPathDepth = 0;
+      key = formatSubPath(key);
+      if (key === false) {
+        return false;
+      } else {
+        actions[PUSH]();
+      }
+    }
+  };
+
+  function maybeUnescapeQuote() {
+    var nextChar = path[index + 1];
+    if (mode === IN_SINGLE_QUOTE && nextChar === "'" || mode === IN_DOUBLE_QUOTE && nextChar === '"') {
+      index++;
+      newChar = '\\' + nextChar;
+      actions[APPEND]();
+      return true;
+    }
+  }
+
+  while (mode != null) {
+    index++;
+    c = path[index];
+
+    if (c === '\\' && maybeUnescapeQuote()) {
+      continue;
+    }
+
+    type = getPathCharType(c);
+    typeMap = pathStateMachine[mode];
+    transition = typeMap[type] || typeMap['else'] || ERROR;
+
+    if (transition === ERROR) {
+      return; // parse error
+    }
+
+    mode = transition[0];
+    action = actions[transition[1]];
+    if (action) {
+      newChar = transition[2];
+      newChar = newChar === undefined ? c : newChar;
+      if (action() === false) {
+        return;
+      }
+    }
+
+    if (mode === AFTER_PATH) {
+      keys.raw = path;
+      return keys;
+    }
+  }
+}
+
+/**
+ * External parse that check for a cache hit first
+ *
+ * @param {String} path
+ * @return {Array|undefined}
+ */
+
+function parsePath(path) {
+  var hit = pathCache.get(path);
+  if (!hit) {
+    hit = parse(path);
+    if (hit) {
+      pathCache.put(path, hit);
+    }
+  }
+  return hit;
+}
+
+/**
+ * Get from an object from a path string
+ *
+ * @param {Object} obj
+ * @param {String} path
+ */
+
+function getPath(obj, path) {
+  return parseExpression$1(path).get(obj);
+}
+
+/**
+ * Warn against setting non-existent root path on a vm.
+ */
+
+var warnNonExistent;
+if (process.env.NODE_ENV !== 'production') {
+  warnNonExistent = function (path, vm) {
+    warn('You are setting a non-existent path "' + path.raw + '" ' + 'on a vm instance. Consider pre-initializing the property ' + 'with the "data" option for more reliable reactivity ' + 'and better performance.', vm);
+  };
+}
+
+/**
+ * Set on an object from a path
+ *
+ * @param {Object} obj
+ * @param {String | Array} path
+ * @param {*} val
+ */
+
+function setPath(obj, path, val) {
+  var original = obj;
+  if (typeof path === 'string') {
+    path = parse(path);
+  }
+  if (!path || !isObject(obj)) {
+    return false;
+  }
+  var last, key;
+  for (var i = 0, l = path.length; i < l; i++) {
+    last = obj;
+    key = path[i];
+    if (key.charAt(0) === '*') {
+      key = parseExpression$1(key.slice(1)).get.call(original, original);
+    }
+    if (i < l - 1) {
+      obj = obj[key];
+      if (!isObject(obj)) {
+        obj = {};
+        if (process.env.NODE_ENV !== 'production' && last._isVue) {
+          warnNonExistent(path, last);
+        }
+        set(last, key, obj);
+      }
+    } else {
+      if (isArray(obj)) {
+        obj.$set(key, val);
+      } else if (key in obj) {
+        obj[key] = val;
+      } else {
+        if (process.env.NODE_ENV !== 'production' && obj._isVue) {
+          warnNonExistent(path, obj);
+        }
+        set(obj, key, val);
+      }
+    }
+  }
+  return true;
+}
+
+var path = Object.freeze({
+  parsePath: parsePath,
+  getPath: getPath,
+  setPath: setPath
+});
+
+var expressionCache = new Cache(1000);
+
+var allowedKeywords = 'Math,Date,this,true,false,null,undefined,Infinity,NaN,' + 'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,' + 'encodeURIComponent,parseInt,parseFloat';
+var allowedKeywordsRE = new RegExp('^(' + allowedKeywords.replace(/,/g, '\\b|') + '\\b)');
+
+// keywords that don't make sense inside expressions
+var improperKeywords = 'break,case,class,catch,const,continue,debugger,default,' + 'delete,do,else,export,extends,finally,for,function,if,' + 'import,in,instanceof,let,return,super,switch,throw,try,' + 'var,while,with,yield,enum,await,implements,package,' + 'protected,static,interface,private,public';
+var improperKeywordsRE = new RegExp('^(' + improperKeywords.replace(/,/g, '\\b|') + '\\b)');
+
+var wsRE = /\s/g;
+var newlineRE = /\n/g;
+var saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\"']|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g;
+var restoreRE = /"(\d+)"/g;
+var pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/;
+var identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g;
+var literalValueRE$1 = /^(?:true|false|null|undefined|Infinity|NaN)$/;
+
+function noop() {}
+
+/**
+ * Save / Rewrite / Restore
+ *
+ * When rewriting paths found in an expression, it is
+ * possible for the same letter sequences to be found in
+ * strings and Object literal property keys. Therefore we
+ * remove and store these parts in a temporary array, and
+ * restore them after the path rewrite.
+ */
+
+var saved = [];
+
+/**
+ * Save replacer
+ *
+ * The save regex can match two possible cases:
+ * 1. An opening object literal
+ * 2. A string
+ * If matched as a plain string, we need to escape its
+ * newlines, since the string needs to be preserved when
+ * generating the function body.
+ *
+ * @param {String} str
+ * @param {String} isString - str if matched as a string
+ * @return {String} - placeholder with index
+ */
+
+function save(str, isString) {
+  var i = saved.length;
+  saved[i] = isString ? str.replace(newlineRE, '\\n') : str;
+  return '"' + i + '"';
+}
+
+/**
+ * Path rewrite replacer
+ *
+ * @param {String} raw
+ * @return {String}
+ */
+
+function rewrite(raw) {
+  var c = raw.charAt(0);
+  var path = raw.slice(1);
+  if (allowedKeywordsRE.test(path)) {
+    return raw;
+  } else {
+    path = path.indexOf('"') > -1 ? path.replace(restoreRE, restore) : path;
+    return c + 'scope.' + path;
+  }
+}
+
+/**
+ * Restore replacer
+ *
+ * @param {String} str
+ * @param {String} i - matched save index
+ * @return {String}
+ */
+
+function restore(str, i) {
+  return saved[i];
+}
+
+/**
+ * Rewrite an expression, prefixing all path accessors with
+ * `scope.` and generate getter/setter functions.
+ *
+ * @param {String} exp
+ * @return {Function}
+ */
+
+function compileGetter(exp) {
+  if (improperKeywordsRE.test(exp)) {
+    process.env.NODE_ENV !== 'production' && warn('Avoid using reserved keywords in expression: ' + exp);
+  }
+  // reset state
+  saved.length = 0;
+  // save strings and object literal keys
+  var body = exp.replace(saveRE, save).replace(wsRE, '');
+  // rewrite all paths
+  // pad 1 space here because the regex matches 1 extra char
+  body = (' ' + body).replace(identRE, rewrite).replace(restoreRE, restore);
+  return makeGetterFn(body);
+}
+
+/**
+ * Build a getter function. Requires eval.
+ *
+ * We isolate the try/catch so it doesn't affect the
+ * optimization of the parse function when it is not called.
+ *
+ * @param {String} body
+ * @return {Function|undefined}
+ */
+
+function makeGetterFn(body) {
+  try {
+    /* eslint-disable no-new-func */
+    return new Function('scope', 'return ' + body + ';');
+    /* eslint-enable no-new-func */
+  } catch (e) {
+    if (process.env.NODE_ENV !== 'production') {
+      /* istanbul ignore if */
+      if (e.toString().match(/unsafe-eval|CSP/)) {
+        warn('It seems you are using the default build of Vue.js in an environment ' + 'with Content Security Policy that prohibits unsafe-eval. ' + 'Use the CSP-compliant build instead: ' + 'http://vuejs.org/guide/installation.html#CSP-compliant-build');
+      } else {
+        warn('Invalid expression. ' + 'Generated function body: ' + body);
+      }
+    }
+    return noop;
+  }
+}
+
+/**
+ * Compile a setter function for the expression.
+ *
+ * @param {String} exp
+ * @return {Function|undefined}
+ */
+
+function compileSetter(exp) {
+  var path = parsePath(exp);
+  if (path) {
+    return function (scope, val) {
+      setPath(scope, path, val);
+    };
+  } else {
+    process.env.NODE_ENV !== 'production' && warn('Invalid setter expression: ' + exp);
+  }
+}
+
+/**
+ * Parse an expression into re-written getter/setters.
+ *
+ * @param {String} exp
+ * @param {Boolean} needSet
+ * @return {Function}
+ */
+
+function parseExpression$1(exp, needSet) {
+  exp = exp.trim();
+  // try cache
+  var hit = expressionCache.get(exp);
+  if (hit) {
+    if (needSet && !hit.set) {
+      hit.set = compileSetter(hit.exp);
+    }
+    return hit;
+  }
+  var res = { exp: exp };
+  res.get = isSimplePath(exp) && exp.indexOf('[') < 0
+  // optimized super simple getter
+  ? makeGetterFn('scope.' + exp)
+  // dynamic getter
+  : compileGetter(exp);
+  if (needSet) {
+    res.set = compileSetter(exp);
+  }
+  expressionCache.put(exp, res);
+  return res;
+}
+
+/**
+ * Check if an expression is a simple path.
+ *
+ * @param {String} exp
+ * @return {Boolean}
+ */
+
+function isSimplePath(exp) {
+  return pathTestRE.test(exp) &&
+  // don't treat literal values as paths
+  !literalValueRE$1.test(exp) &&
+  // Math constants e.g. Math.PI, Math.E etc.
+  exp.slice(0, 5) !== 'Math.';
+}
+
+var expression = Object.freeze({
+  parseExpression: parseExpression$1,
+  isSimplePath: isSimplePath
+});
+
+// we have two separate queues: one for directive updates
+// and one for user watcher registered via $watch().
+// we want to guarantee directive updates to be called
+// before user watchers so that when user watchers are
+// triggered, the DOM would have already been in updated
+// state.
+
+var queue = [];
+var userQueue = [];
+var has = {};
+var circular = {};
+var waiting = false;
+
+/**
+ * Reset the batcher's state.
+ */
+
+function resetBatcherState() {
+  queue.length = 0;
+  userQueue.length = 0;
+  has = {};
+  circular = {};
+  waiting = false;
+}
+
+/**
+ * Flush both queues and run the watchers.
+ */
+
+function flushBatcherQueue() {
+  var _again = true;
+
+  _function: while (_again) {
+    _again = false;
+
+    runBatcherQueue(queue);
+    runBatcherQueue(userQueue);
+    // user watchers triggered more watchers,
+    // keep flushing until it depletes
+    if (queue.length) {
+      _again = true;
+      continue _function;
+    }
+    // dev tool hook
+    /* istanbul ignore if */
+    if (devtools && config.devtools) {
+      devtools.emit('flush');
+    }
+    resetBatcherState();
+  }
+}
+
+/**
+ * Run the watchers in a single queue.
+ *
+ * @param {Array} queue
+ */
+
+function runBatcherQueue(queue) {
+  // do not cache length because more watchers might be pushed
+  // as we run existing watchers
+  for (var i = 0; i < queue.length; i++) {
+    var watcher = queue[i];
+    var id = watcher.id;
+    has[id] = null;
+    watcher.run();
+    // in dev build, check and stop circular updates.
+    if (process.env.NODE_ENV !== 'production' && has[id] != null) {
+      circular[id] = (circular[id] || 0) + 1;
+      if (circular[id] > config._maxUpdateCount) {
+        warn('You may have an infinite update loop for watcher ' + 'with expression "' + watcher.expression + '"', watcher.vm);
+        break;
+      }
+    }
+  }
+  queue.length = 0;
+}
+
+/**
+ * Push a watcher into the watcher queue.
+ * Jobs with duplicate IDs will be skipped unless it's
+ * pushed when the queue is being flushed.
+ *
+ * @param {Watcher} watcher
+ *   properties:
+ *   - {Number} id
+ *   - {Function} run
+ */
+
+function pushWatcher(watcher) {
+  var id = watcher.id;
+  if (has[id] == null) {
+    // push watcher into appropriate queue
+    var q = watcher.user ? userQueue : queue;
+    has[id] = q.length;
+    q.push(watcher);
+    // queue the flush
+    if (!waiting) {
+      waiting = true;
+      nextTick(flushBatcherQueue);
+    }
+  }
+}
+
+var uid$2 = 0;
+
+/**
+ * A watcher parses an expression, collects dependencies,
+ * and fires callback when the expression value changes.
+ * This is used for both the $watch() api and directives.
+ *
+ * @param {Vue} vm
+ * @param {String|Function} expOrFn
+ * @param {Function} cb
+ * @param {Object} options
+ *                 - {Array} filters
+ *                 - {Boolean} twoWay
+ *                 - {Boolean} deep
+ *                 - {Boolean} user
+ *                 - {Boolean} sync
+ *                 - {Boolean} lazy
+ *                 - {Function} [preProcess]
+ *                 - {Function} [postProcess]
+ * @constructor
+ */
+function Watcher(vm, expOrFn, cb, options) {
+  // mix in options
+  if (options) {
+    extend(this, options);
+  }
+  var isFn = typeof expOrFn === 'function';
+  this.vm = vm;
+  vm._watchers.push(this);
+  this.expression = expOrFn;
+  this.cb = cb;
+  this.id = ++uid$2; // uid for batching
+  this.active = true;
+  this.dirty = this.lazy; // for lazy watchers
+  this.deps = [];
+  this.newDeps = [];
+  this.depIds = new _Set();
+  this.newDepIds = new _Set();
+  this.prevError = null; // for async error stacks
+  // parse expression for getter/setter
+  if (isFn) {
+    this.getter = expOrFn;
+    this.setter = undefined;
+  } else {
+    var res = parseExpression$1(expOrFn, this.twoWay);
+    this.getter = res.get;
+    this.setter = res.set;
+  }
+  this.value = this.lazy ? undefined : this.get();
+  // state for avoiding false triggers for deep and Array
+  // watchers during vm._digest()
+  this.queued = this.shallow = false;
+}
+
+/**
+ * Evaluate the getter, and re-collect dependencies.
+ */
+
+Watcher.prototype.get = function () {
+  this.beforeGet();
+  var scope = this.scope || this.vm;
+  var value;
+  try {
+    value = this.getter.call(scope, scope);
+  } catch (e) {
+    if (process.env.NODE_ENV !== 'production' && config.warnExpressionErrors) {
+      warn('Error when evaluating expression ' + '"' + this.expression + '": ' + e.toString(), this.vm);
+    }
+  }
+  // "touch" every property so they are all tracked as
+  // dependencies for deep watching
+  if (this.deep) {
+    traverse(value);
+  }
+  if (this.preProcess) {
+    value = this.preProcess(value);
+  }
+  if (this.filters) {
+    value = scope._applyFilters(value, null, this.filters, false);
+  }
+  if (this.postProcess) {
+    value = this.postProcess(value);
+  }
+  this.afterGet();
+  return value;
+};
+
+/**
+ * Set the corresponding value with the setter.
+ *
+ * @param {*} value
+ */
+
+Watcher.prototype.set = function (value) {
+  var scope = this.scope || this.vm;
+  if (this.filters) {
+    value = scope._applyFilters(value, this.value, this.filters, true);
+  }
+  try {
+    this.setter.call(scope, scope, value);
+  } catch (e) {
+    if (process.env.NODE_ENV !== 'production' && config.warnExpressionErrors) {
+      warn('Error when evaluating setter ' + '"' + this.expression + '": ' + e.toString(), this.vm);
+    }
+  }
+  // two-way sync for v-for alias
+  var forContext = scope.$forContext;
+  if (forContext && forContext.alias === this.expression) {
+    if (forContext.filters) {
+      process.env.NODE_ENV !== 'production' && warn('It seems you are using two-way binding on ' + 'a v-for alias (' + this.expression + '), and the ' + 'v-for has filters. This will not work properly. ' + 'Either remove the filters or use an array of ' + 'objects and bind to object properties instead.', this.vm);
+      return;
+    }
+    forContext._withLock(function () {
+      if (scope.$key) {
+        // original is an object
+        forContext.rawValue[scope.$key] = value;
+      } else {
+        forContext.rawValue.$set(scope.$index, value);
+      }
+    });
+  }
+};
+
+/**
+ * Prepare for dependency collection.
+ */
+
+Watcher.prototype.beforeGet = function () {
+  Dep.target = this;
+};
+
+/**
+ * Add a dependency to this directive.
+ *
+ * @param {Dep} dep
+ */
+
+Watcher.prototype.addDep = function (dep) {
+  var id = dep.id;
+  if (!this.newDepIds.has(id)) {
+    this.newDepIds.add(id);
+    this.newDeps.push(dep);
+    if (!this.depIds.has(id)) {
+      dep.addSub(this);
+    }
+  }
+};
+
+/**
+ * Clean up for dependency collection.
+ */
+
+Watcher.prototype.afterGet = function () {
+  Dep.target = null;
+  var i = this.deps.length;
+  while (i--) {
+    var dep = this.deps[i];
+    if (!this.newDepIds.has(dep.id)) {
+      dep.removeSub(this);
+    }
+  }
+  var tmp = this.depIds;
+  this.depIds = this.newDepIds;
+  this.newDepIds = tmp;
+  this.newDepIds.clear();
+  tmp = this.deps;
+  this.deps = this.newDeps;
+  this.newDeps = tmp;
+  this.newDeps.length = 0;
+};
+
+/**
+ * Subscriber interface.
+ * Will be called when a dependency changes.
+ *
+ * @param {Boolean} shallow
+ */
+
+Watcher.prototype.update = function (shallow) {
+  if (this.lazy) {
+    this.dirty = true;
+  } else if (this.sync || !config.async) {
+    this.run();
+  } else {
+    // if queued, only overwrite shallow with non-shallow,
+    // but not the other way around.
+    this.shallow = this.queued ? shallow ? this.shallow : false : !!shallow;
+    this.queued = true;
+    // record before-push error stack in debug mode
+    /* istanbul ignore if */
+    if (process.env.NODE_ENV !== 'production' && config.debug) {
+      this.prevError = new Error('[vue] async stack trace');
+    }
+    pushWatcher(this);
+  }
+};
+
+/**
+ * Batcher job interface.
+ * Will be called by the batcher.
+ */
+
+Watcher.prototype.run = function () {
+  if (this.active) {
+    var value = this.get();
+    if (value !== this.value ||
+    // Deep watchers and watchers on Object/Arrays should fire even
+    // when the value is the same, because the value may
+    // have mutated; but only do so if this is a
+    // non-shallow update (caused by a vm digest).
+    (isObject(value) || this.deep) && !this.shallow) {
+      // set new value
+      var oldValue = this.value;
+      this.value = value;
+      // in debug + async mode, when a watcher callbacks
+      // throws, we also throw the saved before-push error
+      // so the full cross-tick stack trace is available.
+      var prevError = this.prevError;
+      /* istanbul ignore if */
+      if (process.env.NODE_ENV !== 'production' && config.debug && prevError) {
+        this.prevError = null;
+        try {
+          this.cb.call(this.vm, value, oldValue);
+        } catch (e) {
+          nextTick(function () {
+            throw prevError;
+          }, 0);
+          throw e;
+        }
+      } else {
+        this.cb.call(this.vm, value, oldValue);
+      }
+    }
+    this.queued = this.shallow = false;
+  }
+};
+
+/**
+ * Evaluate the value of the watcher.
+ * This only gets called for lazy watchers.
+ */
+
+Watcher.prototype.evaluate = function () {
+  // avoid overwriting another watcher that is being
+  // collected.
+  var current = Dep.target;
+  this.value = this.get();
+  this.dirty = false;
+  Dep.target = current;
+};
+
+/**
+ * Depend on all deps collected by this watcher.
+ */
+
+Watcher.prototype.depend = function () {
+  var i = this.deps.length;
+  while (i--) {
+    this.deps[i].depend();
+  }
+};
+
+/**
+ * Remove self from all dependencies' subcriber list.
+ */
+
+Watcher.prototype.teardown = function () {
+  if (this.active) {
+    // remove self from vm's watcher list
+    // this is a somewhat expensive operation so we skip it
+    // if the vm is being destroyed or is performing a v-for
+    // re-render (the watcher list is then filtered by v-for).
+    if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) {
+      this.vm._watchers.$remove(this);
+    }
+    var i = this.deps.length;
+    while (i--) {
+      this.deps[i].removeSub(this);
+    }
+    this.active = false;
+    this.vm = this.cb = this.value = null;
+  }
+};
+
+/**
+ * Recrusively traverse an object to evoke all converted
+ * getters, so that every nested property inside the object
+ * is collected as a "deep" dependency.
+ *
+ * @param {*} val
+ */
+
+var seenObjects = new _Set();
+function traverse(val, seen) {
+  var i = undefined,
+      keys = undefined;
+  if (!seen) {
+    seen = seenObjects;
+    seen.clear();
+  }
+  var isA = isArray(val);
+  var isO = isObject(val);
+  if ((isA || isO) && Object.isExtensible(val)) {
+    if (val.__ob__) {
+      var depId = val.__ob__.dep.id;
+      if (seen.has(depId)) {
+        return;
+      } else {
+        seen.add(depId);
+      }
+    }
+    if (isA) {
+      i = val.length;
+      while (i--) traverse(val[i], seen);
+    } else if (isO) {
+      keys = Object.keys(val);
+      i = keys.length;
+      while (i--) traverse(val[keys[i]], seen);
+    }
+  }
+}
+
+var text$1 = {
+
+  bind: function bind() {
+    this.attr = this.el.nodeType === 3 ? 'data' : 'textContent';
+  },
+
+  update: function update(value) {
+    this.el[this.attr] = _toString(value);
+  }
+};
+
+var templateCache = new Cache(1000);
+var idSelectorCache = new Cache(1000);
+
+var map = {
+  efault: [0, '', ''],
+  legend: [1, '<fieldset>', '</fieldset>'],
+  tr: [2, '<table><tbody>', '</tbody></table>'],
+  col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>']
+};
+
+map.td = map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'];
+
+map.option = map.optgroup = [1, '<select multiple="multiple">', '</select>'];
+
+map.thead = map.tbody = map.colgroup = map.caption = map.tfoot = [1, '<table>', '</table>'];
+
+map.g = map.defs = map.symbol = map.use = map.image = map.text = map.circle = map.ellipse = map.line = map.path = map.polygon = map.polyline = map.rect = [1, '<svg ' + 'xmlns="http://www.w3.org/2000/svg" ' + 'xmlns:xlink="http://www.w3.org/1999/xlink" ' + 'xmlns:ev="http://www.w3.org/2001/xml-events"' + 'version="1.1">', '</svg>'];
+
+/**
+ * Check if a node is a supported template node with a
+ * DocumentFragment content.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+function isRealTemplate(node) {
+  return isTemplate(node) && isFragment(node.content);
+}
+
+var tagRE$1 = /<([\w:-]+)/;
+var entityRE = /&#?\w+?;/;
+var commentRE = /<!--/;
+
+/**
+ * Convert a string template to a DocumentFragment.
+ * Determines correct wrapping by tag types. Wrapping
+ * strategy found in jQuery & component/domify.
+ *
+ * @param {String} templateString
+ * @param {Boolean} raw
+ * @return {DocumentFragment}
+ */
+
+function stringToFragment(templateString, raw) {
+  // try a cache hit first
+  var cacheKey = raw ? templateString : templateString.trim();
+  var hit = templateCache.get(cacheKey);
+  if (hit) {
+    return hit;
+  }
+
+  var frag = document.createDocumentFragment();
+  var tagMatch = templateString.match(tagRE$1);
+  var entityMatch = entityRE.test(templateString);
+  var commentMatch = commentRE.test(templateString);
+
+  if (!tagMatch && !entityMatch && !commentMatch) {
+    // text only, return a single text node.
+    frag.appendChild(document.createTextNode(templateString));
+  } else {
+    var tag = tagMatch && tagMatch[1];
+    var wrap = map[tag] || map.efault;
+    var depth = wrap[0];
+    var prefix = wrap[1];
+    var suffix = wrap[2];
+    var node = document.createElement('div');
+
+    node.innerHTML = prefix + templateString + suffix;
+    while (depth--) {
+      node = node.lastChild;
+    }
+
+    var child;
+    /* eslint-disable no-cond-assign */
+    while (child = node.firstChild) {
+      /* eslint-enable no-cond-assign */
+      frag.appendChild(child);
+    }
+  }
+  if (!raw) {
+    trimNode(frag);
+  }
+  templateCache.put(cacheKey, frag);
+  return frag;
+}
+
+/**
+ * Convert a template node to a DocumentFragment.
+ *
+ * @param {Node} node
+ * @return {DocumentFragment}
+ */
+
+function nodeToFragment(node) {
+  // if its a template tag and the browser supports it,
+  // its content is already a document fragment. However, iOS Safari has
+  // bug when using directly cloned template content with touch
+  // events and can cause crashes when the nodes are removed from DOM, so we
+  // have to treat template elements as string templates. (#2805)
+  /* istanbul ignore if */
+  if (isRealTemplate(node)) {
+    return stringToFragment(node.innerHTML);
+  }
+  // script template
+  if (node.tagName === 'SCRIPT') {
+    return stringToFragment(node.textContent);
+  }
+  // normal node, clone it to avoid mutating the original
+  var clonedNode = cloneNode(node);
+  var frag = document.createDocumentFragment();
+  var child;
+  /* eslint-disable no-cond-assign */
+  while (child = clonedNode.firstChild) {
+    /* eslint-enable no-cond-assign */
+    frag.appendChild(child);
+  }
+  trimNode(frag);
+  return frag;
+}
+
+// Test for the presence of the Safari template cloning bug
+// https://bugs.webkit.org/showug.cgi?id=137755
+var hasBrokenTemplate = (function () {
+  /* istanbul ignore else */
+  if (inBrowser) {
+    var a = document.createElement('div');
+    a.innerHTML = '<template>1</template>';
+    return !a.cloneNode(true).firstChild.innerHTML;
+  } else {
+    return false;
+  }
+})();
+
+// Test for IE10/11 textarea placeholder clone bug
+var hasTextareaCloneBug = (function () {
+  /* istanbul ignore else */
+  if (inBrowser) {
+    var t = document.createElement('textarea');
+    t.placeholder = 't';
+    return t.cloneNode(true).value === 't';
+  } else {
+    return false;
+  }
+})();
+
+/**
+ * 1. Deal with Safari cloning nested <template> bug by
+ *    manually cloning all template instances.
+ * 2. Deal with IE10/11 textarea placeholder bug by setting
+ *    the correct value after cloning.
+ *
+ * @param {Element|DocumentFragment} node
+ * @return {Element|DocumentFragment}
+ */
+
+function cloneNode(node) {
+  /* istanbul ignore if */
+  if (!node.querySelectorAll) {
+    return node.cloneNode();
+  }
+  var res = node.cloneNode(true);
+  var i, original, cloned;
+  /* istanbul ignore if */
+  if (hasBrokenTemplate) {
+    var tempClone = res;
+    if (isRealTemplate(node)) {
+      node = node.content;
+      tempClone = res.content;
+    }
+    original = node.querySelectorAll('template');
+    if (original.length) {
+      cloned = tempClone.querySelectorAll('template');
+      i = cloned.length;
+      while (i--) {
+        cloned[i].parentNode.replaceChild(cloneNode(original[i]), cloned[i]);
+      }
+    }
+  }
+  /* istanbul ignore if */
+  if (hasTextareaCloneBug) {
+    if (node.tagName === 'TEXTAREA') {
+      res.value = node.value;
+    } else {
+      original = node.querySelectorAll('textarea');
+      if (original.length) {
+        cloned = res.querySelectorAll('textarea');
+        i = cloned.length;
+        while (i--) {
+          cloned[i].value = original[i].value;
+        }
+      }
+    }
+  }
+  return res;
+}
+
+/**
+ * Process the template option and normalizes it into a
+ * a DocumentFragment that can be used as a partial or a
+ * instance template.
+ *
+ * @param {*} template
+ *        Possible values include:
+ *        - DocumentFragment object
+ *        - Node object of type Template
+ *        - id selector: '#some-template-id'
+ *        - template string: '<div><span>{{msg}}</span></div>'
+ * @param {Boolean} shouldClone
+ * @param {Boolean} raw
+ *        inline HTML interpolation. Do not check for id
+ *        selector and keep whitespace in the string.
+ * @return {DocumentFragment|undefined}
+ */
+
+function parseTemplate(template, shouldClone, raw) {
+  var node, frag;
+
+  // if the template is already a document fragment,
+  // do nothing
+  if (isFragment(template)) {
+    trimNode(template);
+    return shouldClone ? cloneNode(template) : template;
+  }
+
+  if (typeof template === 'string') {
+    // id selector
+    if (!raw && template.charAt(0) === '#') {
+      // id selector can be cached too
+      frag = idSelectorCache.get(template);
+      if (!frag) {
+        node = document.getElementById(template.slice(1));
+        if (node) {
+          frag = nodeToFragment(node);
+          // save selector to cache
+          idSelectorCache.put(template, frag);
+        }
+      }
+    } else {
+      // normal string template
+      frag = stringToFragment(template, raw);
+    }
+  } else if (template.nodeType) {
+    // a direct node
+    frag = nodeToFragment(template);
+  }
+
+  return frag && shouldClone ? cloneNode(frag) : frag;
+}
+
+var template = Object.freeze({
+  cloneNode: cloneNode,
+  parseTemplate: parseTemplate
+});
+
+var html = {
+
+  bind: function bind() {
+    // a comment node means this is a binding for
+    // {{{ inline unescaped html }}}
+    if (this.el.nodeType === 8) {
+      // hold nodes
+      this.nodes = [];
+      // replace the placeholder with proper anchor
+      this.anchor = createAnchor('v-html');
+      replace(this.el, this.anchor);
+    }
+  },
+
+  update: function update(value) {
+    value = _toString(value);
+    if (this.nodes) {
+      this.swap(value);
+    } else {
+      this.el.innerHTML = value;
+    }
+  },
+
+  swap: function swap(value) {
+    // remove old nodes
+    var i = this.nodes.length;
+    while (i--) {
+      remove(this.nodes[i]);
+    }
+    // convert new value to a fragment
+    // do not attempt to retrieve from id selector
+    var frag = parseTemplate(value, true, true);
+    // save a reference to these nodes so we can remove later
+    this.nodes = toArray(frag.childNodes);
+    before(frag, this.anchor);
+  }
+};
+
+/**
+ * Abstraction for a partially-compiled fragment.
+ * Can optionally compile content with a child scope.
+ *
+ * @param {Function} linker
+ * @param {Vue} vm
+ * @param {DocumentFragment} frag
+ * @param {Vue} [host]
+ * @param {Object} [scope]
+ * @param {Fragment} [parentFrag]
+ */
+function Fragment(linker, vm, frag, host, scope, parentFrag) {
+  this.children = [];
+  this.childFrags = [];
+  this.vm = vm;
+  this.scope = scope;
+  this.inserted = false;
+  this.parentFrag = parentFrag;
+  if (parentFrag) {
+    parentFrag.childFrags.push(this);
+  }
+  this.unlink = linker(vm, frag, host, scope, this);
+  var single = this.single = frag.childNodes.length === 1 &&
+  // do not go single mode if the only node is an anchor
+  !frag.childNodes[0].__v_anchor;
+  if (single) {
+    this.node = frag.childNodes[0];
+    this.before = singleBefore;
+    this.remove = singleRemove;
+  } else {
+    this.node = createAnchor('fragment-start');
+    this.end = createAnchor('fragment-end');
+    this.frag = frag;
+    prepend(this.node, frag);
+    frag.appendChild(this.end);
+    this.before = multiBefore;
+    this.remove = multiRemove;
+  }
+  this.node.__v_frag = this;
+}
+
+/**
+ * Call attach/detach for all components contained within
+ * this fragment. Also do so recursively for all child
+ * fragments.
+ *
+ * @param {Function} hook
+ */
+
+Fragment.prototype.callHook = function (hook) {
+  var i, l;
+  for (i = 0, l = this.childFrags.length; i < l; i++) {
+    this.childFrags[i].callHook(hook);
+  }
+  for (i = 0, l = this.children.length; i < l; i++) {
+    hook(this.children[i]);
+  }
+};
+
+/**
+ * Insert fragment before target, single node version
+ *
+ * @param {Node} target
+ * @param {Boolean} withTransition
+ */
+
+function singleBefore(target, withTransition) {
+  this.inserted = true;
+  var method = withTransition !== false ? beforeWithTransition : before;
+  method(this.node, target, this.vm);
+  if (inDoc(this.node)) {
+    this.callHook(attach);
+  }
+}
+
+/**
+ * Remove fragment, single node version
+ */
+
+function singleRemove() {
+  this.inserted = false;
+  var shouldCallRemove = inDoc(this.node);
+  var self = this;
+  this.beforeRemove();
+  removeWithTransition(this.node, this.vm, function () {
+    if (shouldCallRemove) {
+      self.callHook(detach);
+    }
+    self.destroy();
+  });
+}
+
+/**
+ * Insert fragment before target, multi-nodes version
+ *
+ * @param {Node} target
+ * @param {Boolean} withTransition
+ */
+
+function multiBefore(target, withTransition) {
+  this.inserted = true;
+  var vm = this.vm;
+  var method = withTransition !== false ? beforeWithTransition : before;
+  mapNodeRange(this.node, this.end, function (node) {
+    method(node, target, vm);
+  });
+  if (inDoc(this.node)) {
+    this.callHook(attach);
+  }
+}
+
+/**
+ * Remove fragment, multi-nodes version
+ */
+
+function multiRemove() {
+  this.inserted = false;
+  var self = this;
+  var shouldCallRemove = inDoc(this.node);
+  this.beforeRemove();
+  removeNodeRange(this.node, this.end, this.vm, this.frag, function () {
+    if (shouldCallRemove) {
+      self.callHook(detach);
+    }
+    self.destroy();
+  });
+}
+
+/**
+ * Prepare the fragment for removal.
+ */
+
+Fragment.prototype.beforeRemove = function () {
+  var i, l;
+  for (i = 0, l = this.childFrags.length; i < l; i++) {
+    // call the same method recursively on child
+    // fragments, depth-first
+    this.childFrags[i].beforeRemove(false);
+  }
+  for (i = 0, l = this.children.length; i < l; i++) {
+    // Call destroy for all contained instances,
+    // with remove:false and defer:true.
+    // Defer is necessary because we need to
+    // keep the children to call detach hooks
+    // on them.
+    this.children[i].$destroy(false, true);
+  }
+  var dirs = this.unlink.dirs;
+  for (i = 0, l = dirs.length; i < l; i++) {
+    // disable the watchers on all the directives
+    // so that the rendered content stays the same
+    // during removal.
+    dirs[i]._watcher && dirs[i]._watcher.teardown();
+  }
+};
+
+/**
+ * Destroy the fragment.
+ */
+
+Fragment.prototype.destroy = function () {
+  if (this.parentFrag) {
+    this.parentFrag.childFrags.$remove(this);
+  }
+  this.node.__v_frag = null;
+  this.unlink();
+};
+
+/**
+ * Call attach hook for a Vue instance.
+ *
+ * @param {Vue} child
+ */
+
+function attach(child) {
+  if (!child._isAttached && inDoc(child.$el)) {
+    child._callHook('attached');
+  }
+}
+
+/**
+ * Call detach hook for a Vue instance.
+ *
+ * @param {Vue} child
+ */
+
+function detach(child) {
+  if (child._isAttached && !inDoc(child.$el)) {
+    child._callHook('detached');
+  }
+}
+
+var linkerCache = new Cache(5000);
+
+/**
+ * A factory that can be used to create instances of a
+ * fragment. Caches the compiled linker if possible.
+ *
+ * @param {Vue} vm
+ * @param {Element|String} el
+ */
+function FragmentFactory(vm, el) {
+  this.vm = vm;
+  var template;
+  var isString = typeof el === 'string';
+  if (isString || isTemplate(el) && !el.hasAttribute('v-if')) {
+    template = parseTemplate(el, true);
+  } else {
+    template = document.createDocumentFragment();
+    template.appendChild(el);
+  }
+  this.template = template;
+  // linker can be cached, but only for components
+  var linker;
+  var cid = vm.constructor.cid;
+  if (cid > 0) {
+    var cacheId = cid + (isString ? el : getOuterHTML(el));
+    linker = linkerCache.get(cacheId);
+    if (!linker) {
+      linker = compile(template, vm.$options, true);
+      linkerCache.put(cacheId, linker);
+    }
+  } else {
+    linker = compile(template, vm.$options, true);
+  }
+  this.linker = linker;
+}
+
+/**
+ * Create a fragment instance with given host and scope.
+ *
+ * @param {Vue} host
+ * @param {Object} scope
+ * @param {Fragment} parentFrag
+ */
+
+FragmentFactory.prototype.create = function (host, scope, parentFrag) {
+  var frag = cloneNode(this.template);
+  return new Fragment(this.linker, this.vm, frag, host, scope, parentFrag);
+};
+
+var ON = 700;
+var MODEL = 800;
+var BIND = 850;
+var TRANSITION = 1100;
+var EL = 1500;
+var COMPONENT = 1500;
+var PARTIAL = 1750;
+var IF = 2100;
+var FOR = 2200;
+var SLOT = 2300;
+
+var uid$3 = 0;
+
+var vFor = {
+
+  priority: FOR,
+  terminal: true,
+
+  params: ['track-by', 'stagger', 'enter-stagger', 'leave-stagger'],
+
+  bind: function bind() {
+    if (process.env.NODE_ENV !== 'production' && this.el.hasAttribute('v-if')) {
+      warn('<' + this.el.tagName.toLowerCase() + ' v-for="' + this.expression + '" v-if="' + this.el.getAttribute('v-if') + '">: ' + 'Using v-if and v-for on the same element is not recommended - ' + 'consider filtering the source Array instead.', this.vm);
+    }
+
+    // support "item in/of items" syntax
+    var inMatch = this.expression.match(/(.*) (?:in|of) (.*)/);
+    if (inMatch) {
+      var itMatch = inMatch[1].match(/\((.*),(.*)\)/);
+      if (itMatch) {
+        this.iterator = itMatch[1].trim();
+        this.alias = itMatch[2].trim();
+      } else {
+        this.alias = inMatch[1].trim();
+      }
+      this.expression = inMatch[2];
+    }
+
+    if (!this.alias) {
+      process.env.NODE_ENV !== 'production' && warn('Invalid v-for expression "' + this.descriptor.raw + '": ' + 'alias is required.', this.vm);
+      return;
+    }
+
+    // uid as a cache identifier
+    this.id = '__v-for__' + ++uid$3;
+
+    // check if this is an option list,
+    // so that we know if we need to update the <select>'s
+    // v-model when the option list has changed.
+    // because v-model has a lower priority than v-for,
+    // the v-model is not bound here yet, so we have to
+    // retrive it in the actual updateModel() function.
+    var tag = this.el.tagName;
+    this.isOption = (tag === 'OPTION' || tag === 'OPTGROUP') && this.el.parentNode.tagName === 'SELECT';
+
+    // setup anchor nodes
+    this.start = createAnchor('v-for-start');
+    this.end = createAnchor('v-for-end');
+    replace(this.el, this.end);
+    before(this.start, this.end);
+
+    // cache
+    this.cache = Object.create(null);
+
+    // fragment factory
+    this.factory = new FragmentFactory(this.vm, this.el);
+  },
+
+  update: function update(data) {
+    this.diff(data);
+    this.updateRef();
+    this.updateModel();
+  },
+
+  /**
+   * Diff, based on new data and old data, determine the
+   * minimum amount of DOM manipulations needed to make the
+   * DOM reflect the new data Array.
+   *
+   * The algorithm diffs the new data Array by storing a
+   * hidden reference to an owner vm instance on previously
+   * seen data. This allows us to achieve O(n) which is
+   * better than a levenshtein distance based algorithm,
+   * which is O(m * n).
+   *
+   * @param {Array} data
+   */
+
+  diff: function diff(data) {
+    // check if the Array was converted from an Object
+    var item = data[0];
+    var convertedFromObject = this.fromObject = isObject(item) && hasOwn(item, '$key') && hasOwn(item, '$value');
+
+    var trackByKey = this.params.trackBy;
+    var oldFrags = this.frags;
+    var frags = this.frags = new Array(data.length);
+    var alias = this.alias;
+    var iterator = this.iterator;
+    var start = this.start;
+    var end = this.end;
+    var inDocument = inDoc(start);
+    var init = !oldFrags;
+    var i, l, frag, key, value, primitive;
+
+    // First pass, go through the new Array and fill up
+    // the new frags array. If a piece of data has a cached
+    // instance for it, we reuse it. Otherwise build a new
+    // instance.
+    for (i = 0, l = data.length; i < l; i++) {
+      item = data[i];
+      key = convertedFromObject ? item.$key : null;
+      value = convertedFromObject ? item.$value : item;
+      primitive = !isObject(value);
+      frag = !init && this.getCachedFrag(value, i, key);
+      if (frag) {
+        // reusable fragment
+        frag.reused = true;
+        // update $index
+        frag.scope.$index = i;
+        // update $key
+        if (key) {
+          frag.scope.$key = key;
+        }
+        // update iterator
+        if (iterator) {
+          frag.scope[iterator] = key !== null ? key : i;
+        }
+        // update data for track-by, object repeat &
+        // primitive values.
+        if (trackByKey || convertedFromObject || primitive) {
+          withoutConversion(function () {
+            frag.scope[alias] = value;
+          });
+        }
+      } else {
+        // new instance
+        frag = this.create(value, alias, i, key);
+        frag.fresh = !init;
+      }
+      frags[i] = frag;
+      if (init) {
+        frag.before(end);
+      }
+    }
+
+    // we're done for the initial render.
+    if (init) {
+      return;
+    }
+
+    // Second pass, go through the old fragments and
+    // destroy those who are not reused (and remove them
+    // from cache)
+    var removalIndex = 0;
+    var totalRemoved = oldFrags.length - frags.length;
+    // when removing a large number of fragments, watcher removal
+    // turns out to be a perf bottleneck, so we batch the watcher
+    // removals into a single filter call!
+    this.vm._vForRemoving = true;
+    for (i = 0, l = oldFrags.length; i < l; i++) {
+      frag = oldFrags[i];
+      if (!frag.reused) {
+        this.deleteCachedFrag(frag);
+        this.remove(frag, removalIndex++, totalRemoved, inDocument);
+      }
+    }
+    this.vm._vForRemoving = false;
+    if (removalIndex) {
+      this.vm._watchers = this.vm._watchers.filter(function (w) {
+        return w.active;
+      });
+    }
+
+    // Final pass, move/insert new fragments into the
+    // right place.
+    var targetPrev, prevEl, currentPrev;
+    var insertionIndex = 0;
+    for (i = 0, l = frags.length; i < l; i++) {
+      frag = frags[i];
+      // this is the frag that we should be after
+      targetPrev = frags[i - 1];
+      prevEl = targetPrev ? targetPrev.staggerCb ? targetPrev.staggerAnchor : targetPrev.end || targetPrev.node : start;
+      if (frag.reused && !frag.staggerCb) {
+        currentPrev = findPrevFrag(frag, start, this.id);
+        if (currentPrev !== targetPrev && (!currentPrev ||
+        // optimization for moving a single item.
+        // thanks to suggestions by @livoras in #1807
+        findPrevFrag(currentPrev, start, this.id) !== targetPrev)) {
+          this.move(frag, prevEl);
+        }
+      } else {
+        // new instance, or still in stagger.
+        // insert with updated stagger index.
+        this.insert(frag, insertionIndex++, prevEl, inDocument);
+      }
+      frag.reused = frag.fresh = false;
+    }
+  },
+
+  /**
+   * Create a new fragment instance.
+   *
+   * @param {*} value
+   * @param {String} alias
+   * @param {Number} index
+   * @param {String} [key]
+   * @return {Fragment}
+   */
+
+  create: function create(value, alias, index, key) {
+    var host = this._host;
+    // create iteration scope
+    var parentScope = this._scope || this.vm;
+    var scope = Object.create(parentScope);
+    // ref holder for the scope
+    scope.$refs = Object.create(parentScope.$refs);
+    scope.$els = Object.create(parentScope.$els);
+    // make sure point $parent to parent scope
+    scope.$parent = parentScope;
+    // for two-way binding on alias
+    scope.$forContext = this;
+    // define scope properties
+    // important: define the scope alias without forced conversion
+    // so that frozen data structures remain non-reactive.
+    withoutConversion(function () {
+      defineReactive(scope, alias, value);
+    });
+    defineReactive(scope, '$index', index);
+    if (key) {
+      defineReactive(scope, '$key', key);
+    } else if (scope.$key) {
+      // avoid accidental fallback
+      def(scope, '$key', null);
+    }
+    if (this.iterator) {
+      defineReactive(scope, this.iterator, key !== null ? key : index);
+    }
+    var frag = this.factory.create(host, scope, this._frag);
+    frag.forId = this.id;
+    this.cacheFrag(value, frag, index, key);
+    return frag;
+  },
+
+  /**
+   * Update the v-ref on owner vm.
+   */
+
+  updateRef: function updateRef() {
+    var ref = this.descriptor.ref;
+    if (!ref) return;
+    var hash = (this._scope || this.vm).$refs;
+    var refs;
+    if (!this.fromObject) {
+      refs = this.frags.map(findVmFromFrag);
+    } else {
+      refs = {};
+      this.frags.forEach(function (frag) {
+        refs[frag.scope.$key] = findVmFromFrag(frag);
+      });
+    }
+    hash[ref] = refs;
+  },
+
+  /**
+   * For option lists, update the containing v-model on
+   * parent <select>.
+   */
+
+  updateModel: function updateModel() {
+    if (this.isOption) {
+      var parent = this.start.parentNode;
+      var model = parent && parent.__v_model;
+      if (model) {
+        model.forceUpdate();
+      }
+    }
+  },
+
+  /**
+   * Insert a fragment. Handles staggering.
+   *
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {Node} prevEl
+   * @param {Boolean} inDocument
+   */
+
+  insert: function insert(frag, index, prevEl, inDocument) {
+    if (frag.staggerCb) {
+      frag.staggerCb.cancel();
+      frag.staggerCb = null;
+    }
+    var staggerAmount = this.getStagger(frag, index, null, 'enter');
+    if (inDocument && staggerAmount) {
+      // create an anchor and insert it synchronously,
+      // so that we can resolve the correct order without
+      // worrying about some elements not inserted yet
+      var anchor = frag.staggerAnchor;
+      if (!anchor) {
+        anchor = frag.staggerAnchor = createAnchor('stagger-anchor');
+        anchor.__v_frag = frag;
+      }
+      after(anchor, prevEl);
+      var op = frag.staggerCb = cancellable(function () {
+        frag.staggerCb = null;
+        frag.before(anchor);
+        remove(anchor);
+      });
+      setTimeout(op, staggerAmount);
+    } else {
+      var target = prevEl.nextSibling;
+      /* istanbul ignore if */
+      if (!target) {
+        // reset end anchor position in case the position was messed up
+        // by an external drag-n-drop library.
+        after(this.end, prevEl);
+        target = this.end;
+      }
+      frag.before(target);
+    }
+  },
+
+  /**
+   * Remove a fragment. Handles staggering.
+   *
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {Number} total
+   * @param {Boolean} inDocument
+   */
+
+  remove: function remove(frag, index, total, inDocument) {
+    if (frag.staggerCb) {
+      frag.staggerCb.cancel();
+      frag.staggerCb = null;
+      // it's not possible for the same frag to be removed
+      // twice, so if we have a pending stagger callback,
+      // it means this frag is queued for enter but removed
+      // before its transition started. Since it is already
+      // destroyed, we can just leave it in detached state.
+      return;
+    }
+    var staggerAmount = this.getStagger(frag, index, total, 'leave');
+    if (inDocument && staggerAmount) {
+      var op = frag.staggerCb = cancellable(function () {
+        frag.staggerCb = null;
+        frag.remove();
+      });
+      setTimeout(op, staggerAmount);
+    } else {
+      frag.remove();
+    }
+  },
+
+  /**
+   * Move a fragment to a new position.
+   * Force no transition.
+   *
+   * @param {Fragment} frag
+   * @param {Node} prevEl
+   */
+
+  move: function move(frag, prevEl) {
+    // fix a common issue with Sortable:
+    // if prevEl doesn't have nextSibling, this means it's
+    // been dragged after the end anchor. Just re-position
+    // the end anchor to the end of the container.
+    /* istanbul ignore if */
+    if (!prevEl.nextSibling) {
+      this.end.parentNode.appendChild(this.end);
+    }
+    frag.before(prevEl.nextSibling, false);
+  },
+
+  /**
+   * Cache a fragment using track-by or the object key.
+   *
+   * @param {*} value
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {String} [key]
+   */
+
+  cacheFrag: function cacheFrag(value, frag, index, key) {
+    var trackByKey = this.params.trackBy;
+    var cache = this.cache;
+    var primitive = !isObject(value);
+    var id;
+    if (key || trackByKey || primitive) {
+      id = getTrackByKey(index, key, value, trackByKey);
+      if (!cache[id]) {
+        cache[id] = frag;
+      } else if (trackByKey !== '$index') {
+        process.env.NODE_ENV !== 'production' && this.warnDuplicate(value);
+      }
+    } else {
+      id = this.id;
+      if (hasOwn(value, id)) {
+        if (value[id] === null) {
+          value[id] = frag;
+        } else {
+          process.env.NODE_ENV !== 'production' && this.warnDuplicate(value);
+        }
+      } else if (Object.isExtensible(value)) {
+        def(value, id, frag);
+      } else if (process.env.NODE_ENV !== 'production') {
+        warn('Frozen v-for objects cannot be automatically tracked, make sure to ' + 'provide a track-by key.');
+      }
+    }
+    frag.raw = value;
+  },
+
+  /**
+   * Get a cached fragment from the value/index/key
+   *
+   * @param {*} value
+   * @param {Number} index
+   * @param {String} key
+   * @return {Fragment}
+   */
+
+  getCachedFrag: function getCachedFrag(value, index, key) {
+    var trackByKey = this.params.trackBy;
+    var primitive = !isObject(value);
+    var frag;
+    if (key || trackByKey || primitive) {
+      var id = getTrackByKey(index, key, value, trackByKey);
+      frag = this.cache[id];
+    } else {
+      frag = value[this.id];
+    }
+    if (frag && (frag.reused || frag.fresh)) {
+      process.env.NODE_ENV !== 'production' && this.warnDuplicate(value);
+    }
+    return frag;
+  },
+
+  /**
+   * Delete a fragment from cache.
+   *
+   * @param {Fragment} frag
+   */
+
+  deleteCachedFrag: function deleteCachedFrag(frag) {
+    var value = frag.raw;
+    var trackByKey = this.params.trackBy;
+    var scope = frag.scope;
+    var index = scope.$index;
+    // fix #948: avoid accidentally fall through to
+    // a parent repeater which happens to have $key.
+    var key = hasOwn(scope, '$key') && scope.$key;
+    var primitive = !isObject(value);
+    if (trackByKey || key || primitive) {
+      var id = getTrackByKey(index, key, value, trackByKey);
+      this.cache[id] = null;
+    } else {
+      value[this.id] = null;
+      frag.raw = null;
+    }
+  },
+
+  /**
+   * Get the stagger amount for an insertion/removal.
+   *
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {Number} total
+   * @param {String} type
+   */
+
+  getStagger: function getStagger(frag, index, total, type) {
+    type = type + 'Stagger';
+    var trans = frag.node.__v_trans;
+    var hooks = trans && trans.hooks;
+    var hook = hooks && (hooks[type] || hooks.stagger);
+    return hook ? hook.call(frag, index, total) : index * parseInt(this.params[type] || this.params.stagger, 10);
+  },
+
+  /**
+   * Pre-process the value before piping it through the
+   * filters. This is passed to and called by the watcher.
+   */
+
+  _preProcess: function _preProcess(value) {
+    // regardless of type, store the un-filtered raw value.
+    this.rawValue = value;
+    return value;
+  },
+
+  /**
+   * Post-process the value after it has been piped through
+   * the filters. This is passed to and called by the watcher.
+   *
+   * It is necessary for this to be called during the
+   * watcher's dependency collection phase because we want
+   * the v-for to update when the source Object is mutated.
+   */
+
+  _postProcess: function _postProcess(value) {
+    if (isArray(value)) {
+      return value;
+    } else if (isPlainObject(value)) {
+      // convert plain object to array.
+      var keys = Object.keys(value);
+      var i = keys.length;
+      var res = new Array(i);
+      var key;
+      while (i--) {
+        key = keys[i];
+        res[i] = {
+          $key: key,
+          $value: value[key]
+        };
+      }
+      return res;
+    } else {
+      if (typeof value === 'number' && !isNaN(value)) {
+        value = range(value);
+      }
+      return value || [];
+    }
+  },
+
+  unbind: function unbind() {
+    if (this.descriptor.ref) {
+      (this._scope || this.vm).$refs[this.descriptor.ref] = null;
+    }
+    if (this.frags) {
+      var i = this.frags.length;
+      var frag;
+      while (i--) {
+        frag = this.frags[i];
+        this.deleteCachedFrag(frag);
+        frag.destroy();
+      }
+    }
+  }
+};
+
+/**
+ * Helper to find the previous element that is a fragment
+ * anchor. This is necessary because a destroyed frag's
+ * element could still be lingering in the DOM before its
+ * leaving transition finishes, but its inserted flag
+ * should have been set to false so we can skip them.
+ *
+ * If this is a block repeat, we want to make sure we only
+ * return frag that is bound to this v-for. (see #929)
+ *
+ * @param {Fragment} frag
+ * @param {Comment|Text} anchor
+ * @param {String} id
+ * @return {Fragment}
+ */
+
+function findPrevFrag(frag, anchor, id) {
+  var el = frag.node.previousSibling;
+  /* istanbul ignore if */
+  if (!el) return;
+  frag = el.__v_frag;
+  while ((!frag || frag.forId !== id || !frag.inserted) && el !== anchor) {
+    el = el.previousSibling;
+    /* istanbul ignore if */
+    if (!el) return;
+    frag = el.__v_frag;
+  }
+  return frag;
+}
+
+/**
+ * Create a range array from given number.
+ *
+ * @param {Number} n
+ * @return {Array}
+ */
+
+function range(n) {
+  var i = -1;
+  var ret = new Array(Math.floor(n));
+  while (++i < n) {
+    ret[i] = i;
+  }
+  return ret;
+}
+
+/**
+ * Get the track by key for an item.
+ *
+ * @param {Number} index
+ * @param {String} key
+ * @param {*} value
+ * @param {String} [trackByKey]
+ */
+
+function getTrackByKey(index, key, value, trackByKey) {
+  return trackByKey ? trackByKey === '$index' ? index : trackByKey.charAt(0).match(/\w/) ? getPath(value, trackByKey) : value[trackByKey] : key || value;
+}
+
+if (process.env.NODE_ENV !== 'production') {
+  vFor.warnDuplicate = function (value) {
+    warn('Duplicate value found in v-for="' + this.descriptor.raw + '": ' + JSON.stringify(value) + '. Use track-by="$index" if ' + 'you are expecting duplicate values.', this.vm);
+  };
+}
+
+/**
+ * Find a vm from a fragment.
+ *
+ * @param {Fragment} frag
+ * @return {Vue|undefined}
+ */
+
+function findVmFromFrag(frag) {
+  var node = frag.node;
+  // handle multi-node frag
+  if (frag.end) {
+    while (!node.__vue__ && node !== frag.end && node.nextSibling) {
+      node = node.nextSibling;
+    }
+  }
+  return node.__vue__;
+}
+
+var vIf = {
+
+  priority: IF,
+  terminal: true,
+
+  bind: function bind() {
+    var el = this.el;
+    if (!el.__vue__) {
+      // check else block
+      var next = el.nextElementSibling;
+      if (next && getAttr(next, 'v-else') !== null) {
+        remove(next);
+        this.elseEl = next;
+      }
+      // check main block
+      this.anchor = createAnchor('v-if');
+      replace(el, this.anchor);
+    } else {
+      process.env.NODE_ENV !== 'production' && warn('v-if="' + this.expression + '" cannot be ' + 'used on an instance root element.', this.vm);
+      this.invalid = true;
+    }
+  },
+
+  update: function update(value) {
+    if (this.invalid) return;
+    if (value) {
+      if (!this.frag) {
+        this.insert();
+      }
+    } else {
+      this.remove();
+    }
+  },
+
+  insert: function insert() {
+    if (this.elseFrag) {
+      this.elseFrag.remove();
+      this.elseFrag = null;
+    }
+    // lazy init factory
+    if (!this.factory) {
+      this.factory = new FragmentFactory(this.vm, this.el);
+    }
+    this.frag = this.factory.create(this._host, this._scope, this._frag);
+    this.frag.before(this.anchor);
+  },
+
+  remove: function remove() {
+    if (this.frag) {
+      this.frag.remove();
+      this.frag = null;
+    }
+    if (this.elseEl && !this.elseFrag) {
+      if (!this.elseFactory) {
+        this.elseFactory = new FragmentFactory(this.elseEl._context || this.vm, this.elseEl);
+      }
+      this.elseFrag = this.elseFactory.create(this._host, this._scope, this._frag);
+      this.elseFrag.before(this.anchor);
+    }
+  },
+
+  unbind: function unbind() {
+    if (this.frag) {
+      this.frag.destroy();
+    }
+    if (this.elseFrag) {
+      this.elseFrag.destroy();
+    }
+  }
+};
+
+var show = {
+
+  bind: function bind() {
+    // check else block
+    var next = this.el.nextElementSibling;
+    if (next && getAttr(next, 'v-else') !== null) {
+      this.elseEl = next;
+    }
+  },
+
+  update: function update(value) {
+    this.apply(this.el, value);
+    if (this.elseEl) {
+      this.apply(this.elseEl, !value);
+    }
+  },
+
+  apply: function apply(el, value) {
+    if (inDoc(el)) {
+      applyTransition(el, value ? 1 : -1, toggle, this.vm);
+    } else {
+      toggle();
+    }
+    function toggle() {
+      el.style.display = value ? '' : 'none';
+    }
+  }
+};
+
+var text$2 = {
+
+  bind: function bind() {
+    var self = this;
+    var el = this.el;
+    var isRange = el.type === 'range';
+    var lazy = this.params.lazy;
+    var number = this.params.number;
+    var debounce = this.params.debounce;
+
+    // handle composition events.
+    //   http://blog.evanyou.me/2014/01/03/composition-event/
+    // skip this for Android because it handles composition
+    // events quite differently. Android doesn't trigger
+    // composition events for language input methods e.g.
+    // Chinese, but instead triggers them for spelling
+    // suggestions... (see Discussion/#162)
+    var composing = false;
+    if (!isAndroid && !isRange) {
+      this.on('compositionstart', function () {
+        composing = true;
+      });
+      this.on('compositionend', function () {
+        composing = false;
+        // in IE11 the "compositionend" event fires AFTER
+        // the "input" event, so the input handler is blocked
+        // at the end... have to call it here.
+        //
+        // #1327: in lazy mode this is unecessary.
+        if (!lazy) {
+          self.listener();
+        }
+      });
+    }
+
+    // prevent messing with the input when user is typing,
+    // and force update on blur.
+    this.focused = false;
+    if (!isRange && !lazy) {
+      this.on('focus', function () {
+        self.focused = true;
+      });
+      this.on('blur', function () {
+        self.focused = false;
+        // do not sync value after fragment removal (#2017)
+        if (!self._frag || self._frag.inserted) {
+          self.rawListener();
+        }
+      });
+    }
+
+    // Now attach the main listener
+    this.listener = this.rawListener = function () {
+      if (composing || !self._bound) {
+        return;
+      }
+      var val = number || isRange ? toNumber(el.value) : el.value;
+      self.set(val);
+      // force update on next tick to avoid lock & same value
+      // also only update when user is not typing
+      nextTick(function () {
+        if (self._bound && !self.focused) {
+          self.update(self._watcher.value);
+        }
+      });
+    };
+
+    // apply debounce
+    if (debounce) {
+      this.listener = _debounce(this.listener, debounce);
+    }
+
+    // Support jQuery events, since jQuery.trigger() doesn't
+    // trigger native events in some cases and some plugins
+    // rely on $.trigger()
+    //
+    // We want to make sure if a listener is attached using
+    // jQuery, it is also removed with jQuery, that's why
+    // we do the check for each directive instance and
+    // store that check result on itself. This also allows
+    // easier test coverage control by unsetting the global
+    // jQuery variable in tests.
+    this.hasjQuery = typeof jQuery === 'function';
+    if (this.hasjQuery) {
+      var method = jQuery.fn.on ? 'on' : 'bind';
+      jQuery(el)[method]('change', this.rawListener);
+      if (!lazy) {
+        jQuery(el)[method]('input', this.listener);
+      }
+    } else {
+      this.on('change', this.rawListener);
+      if (!lazy) {
+        this.on('input', this.listener);
+      }
+    }
+
+    // IE9 doesn't fire input event on backspace/del/cut
+    if (!lazy && isIE9) {
+      this.on('cut', function () {
+        nextTick(self.listener);
+      });
+      this.on('keyup', function (e) {
+        if (e.keyCode === 46 || e.keyCode === 8) {
+          self.listener();
+        }
+      });
+    }
+
+    // set initial value if present
+    if (el.hasAttribute('value') || el.tagName === 'TEXTAREA' && el.value.trim()) {
+      this.afterBind = this.listener;
+    }
+  },
+
+  update: function update(value) {
+    // #3029 only update when the value changes. This prevent
+    // browsers from overwriting values like selectionStart
+    value = _toString(value);
+    if (value !== this.el.value) this.el.value = value;
+  },
+
+  unbind: function unbind() {
+    var el = this.el;
+    if (this.hasjQuery) {
+      var method = jQuery.fn.off ? 'off' : 'unbind';
+      jQuery(el)[method]('change', this.listener);
+      jQuery(el)[method]('input', this.listener);
+    }
+  }
+};
+
+var radio = {
+
+  bind: function bind() {
+    var self = this;
+    var el = this.el;
+
+    this.getValue = function () {
+      // value overwrite via v-bind:value
+      if (el.hasOwnProperty('_value')) {
+        return el._value;
+      }
+      var val = el.value;
+      if (self.params.number) {
+        val = toNumber(val);
+      }
+      return val;
+    };
+
+    this.listener = function () {
+      self.set(self.getValue());
+    };
+    this.on('change', this.listener);
+
+    if (el.hasAttribute('checked')) {
+      this.afterBind = this.listener;
+    }
+  },
+
+  update: function update(value) {
+    this.el.checked = looseEqual(value, this.getValue());
+  }
+};
+
+var select = {
+
+  bind: function bind() {
+    var _this = this;
+
+    var self = this;
+    var el = this.el;
+
+    // method to force update DOM using latest value.
+    this.forceUpdate = function () {
+      if (self._watcher) {
+        self.update(self._watcher.get());
+      }
+    };
+
+    // check if this is a multiple select
+    var multiple = this.multiple = el.hasAttribute('multiple');
+
+    // attach listener
+    this.listener = function () {
+      var value = getValue(el, multiple);
+      value = self.params.number ? isArray(value) ? value.map(toNumber) : toNumber(value) : value;
+      self.set(value);
+    };
+    this.on('change', this.listener);
+
+    // if has initial value, set afterBind
+    var initValue = getValue(el, multiple, true);
+    if (multiple && initValue.length || !multiple && initValue !== null) {
+      this.afterBind = this.listener;
+    }
+
+    // All major browsers except Firefox resets
+    // selectedIndex with value -1 to 0 when the element
+    // is appended to a new parent, therefore we have to
+    // force a DOM update whenever that happens...
+    this.vm.$on('hook:attached', function () {
+      nextTick(_this.forceUpdate);
+    });
+    if (!inDoc(el)) {
+      nextTick(this.forceUpdate);
+    }
+  },
+
+  update: function update(value) {
+    var el = this.el;
+    el.selectedIndex = -1;
+    var multi = this.multiple && isArray(value);
+    var options = el.options;
+    var i = options.length;
+    var op, val;
+    while (i--) {
+      op = options[i];
+      val = op.hasOwnProperty('_value') ? op._value : op.value;
+      /* eslint-disable eqeqeq */
+      op.selected = multi ? indexOf$1(value, val) > -1 : looseEqual(value, val);
+      /* eslint-enable eqeqeq */
+    }
+  },
+
+  unbind: function unbind() {
+    /* istanbul ignore next */
+    this.vm.$off('hook:attached', this.forceUpdate);
+  }
+};
+
+/**
+ * Get select value
+ *
+ * @param {SelectElement} el
+ * @param {Boolean} multi
+ * @param {Boolean} init
+ * @return {Array|*}
+ */
+
+function getValue(el, multi, init) {
+  var res = multi ? [] : null;
+  var op, val, selected;
+  for (var i = 0, l = el.options.length; i < l; i++) {
+    op = el.options[i];
+    selected = init ? op.hasAttribute('selected') : op.selected;
+    if (selected) {
+      val = op.hasOwnProperty('_value') ? op._value : op.value;
+      if (multi) {
+        res.push(val);
+      } else {
+        return val;
+      }
+    }
+  }
+  return res;
+}
+
+/**
+ * Native Array.indexOf uses strict equal, but in this
+ * case we need to match string/numbers with custom equal.
+ *
+ * @param {Array} arr
+ * @param {*} val
+ */
+
+function indexOf$1(arr, val) {
+  var i = arr.length;
+  while (i--) {
+    if (looseEqual(arr[i], val)) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+var checkbox = {
+
+  bind: function bind() {
+    var self = this;
+    var el = this.el;
+
+    this.getValue = function () {
+      return el.hasOwnProperty('_value') ? el._value : self.params.number ? toNumber(el.value) : el.value;
+    };
+
+    function getBooleanValue() {
+      var val = el.checked;
+      if (val && el.hasOwnProperty('_trueValue')) {
+        return el._trueValue;
+      }
+      if (!val && el.hasOwnProperty('_falseValue')) {
+        return el._falseValue;
+      }
+      return val;
+    }
+
+    this.listener = function () {
+      var model = self._watcher.get();
+      if (isArray(model)) {
+        var val = self.getValue();
+        var i = indexOf(model, val);
+        if (el.checked) {
+          if (i < 0) {
+            self.set(model.concat(val));
+          }
+        } else if (i > -1) {
+          self.set(model.slice(0, i).concat(model.slice(i + 1)));
+        }
+      } else {
+        self.set(getBooleanValue());
+      }
+    };
+
+    this.on('change', this.listener);
+    if (el.hasAttribute('checked')) {
+      this.afterBind = this.listener;
+    }
+  },
+
+  update: function update(value) {
+    var el = this.el;
+    if (isArray(value)) {
+      el.checked = indexOf(value, this.getValue()) > -1;
+    } else {
+      if (el.hasOwnProperty('_trueValue')) {
+        el.checked = looseEqual(value, el._trueValue);
+      } else {
+        el.checked = !!value;
+      }
+    }
+  }
+};
+
+var handlers = {
+  text: text$2,
+  radio: radio,
+  select: select,
+  checkbox: checkbox
+};
+
+var model = {
+
+  priority: MODEL,
+  twoWay: true,
+  handlers: handlers,
+  params: ['lazy', 'number', 'debounce'],
+
+  /**
+   * Possible elements:
+   *   <select>
+   *   <textarea>
+   *   <input type="*">
+   *     - text
+   *     - checkbox
+   *     - radio
+   *     - number
+   */
+
+  bind: function bind() {
+    // friendly warning...
+    this.checkFilters();
+    if (this.hasRead && !this.hasWrite) {
+      process.env.NODE_ENV !== 'production' && warn('It seems you are using a read-only filter with ' + 'v-model="' + this.descriptor.raw + '". ' + 'You might want to use a two-way filter to ensure correct behavior.', this.vm);
+    }
+    var el = this.el;
+    var tag = el.tagName;
+    var handler;
+    if (tag === 'INPUT') {
+      handler = handlers[el.type] || handlers.text;
+    } else if (tag === 'SELECT') {
+      handler = handlers.select;
+    } else if (tag === 'TEXTAREA') {
+      handler = handlers.text;
+    } else {
+      process.env.NODE_ENV !== 'production' && warn('v-model does not support element type: ' + tag, this.vm);
+      return;
+    }
+    el.__v_model = this;
+    handler.bind.call(this);
+    this.update = handler.update;
+    this._unbind = handler.unbind;
+  },
+
+  /**
+   * Check read/write filter stats.
+   */
+
+  checkFilters: function checkFilters() {
+    var filters = this.filters;
+    if (!filters) return;
+    var i = filters.length;
+    while (i--) {
+      var filter = resolveAsset(this.vm.$options, 'filters', filters[i].name);
+      if (typeof filter === 'function' || filter.read) {
+        this.hasRead = true;
+      }
+      if (filter.write) {
+        this.hasWrite = true;
+      }
+    }
+  },
+
+  unbind: function unbind() {
+    this.el.__v_model = null;
+    this._unbind && this._unbind();
+  }
+};
+
+// keyCode aliases
+var keyCodes = {
+  esc: 27,
+  tab: 9,
+  enter: 13,
+  space: 32,
+  'delete': [8, 46],
+  up: 38,
+  left: 37,
+  right: 39,
+  down: 40
+};
+
+function keyFilter(handler, keys) {
+  var codes = keys.map(function (key) {
+    var charCode = key.charCodeAt(0);
+    if (charCode > 47 && charCode < 58) {
+      return parseInt(key, 10);
+    }
+    if (key.length === 1) {
+      charCode = key.toUpperCase().charCodeAt(0);
+      if (charCode > 64 && charCode < 91) {
+        return charCode;
+      }
+    }
+    return keyCodes[key];
+  });
+  codes = [].concat.apply([], codes);
+  return function keyHandler(e) {
+    if (codes.indexOf(e.keyCode) > -1) {
+      return handler.call(this, e);
+    }
+  };
+}
+
+function stopFilter(handler) {
+  return function stopHandler(e) {
+    e.stopPropagation();
+    return handler.call(this, e);
+  };
+}
+
+function preventFilter(handler) {
+  return function preventHandler(e) {
+    e.preventDefault();
+    return handler.call(this, e);
+  };
+}
+
+function selfFilter(handler) {
+  return function selfHandler(e) {
+    if (e.target === e.currentTarget) {
+      return handler.call(this, e);
+    }
+  };
+}
+
+var on$1 = {
+
+  priority: ON,
+  acceptStatement: true,
+  keyCodes: keyCodes,
+
+  bind: function bind() {
+    // deal with iframes
+    if (this.el.tagName === 'IFRAME' && this.arg !== 'load') {
+      var self = this;
+      this.iframeBind = function () {
+        on(self.el.contentWindow, self.arg, self.handler, self.modifiers.capture);
+      };
+      this.on('load', this.iframeBind);
+    }
+  },
+
+  update: function update(handler) {
+    // stub a noop for v-on with no value,
+    // e.g. @mousedown.prevent
+    if (!this.descriptor.raw) {
+      handler = function () {};
+    }
+
+    if (typeof handler !== 'function') {
+      process.env.NODE_ENV !== 'production' && warn('v-on:' + this.arg + '="' + this.expression + '" expects a function value, ' + 'got ' + handler, this.vm);
+      return;
+    }
+
+    // apply modifiers
+    if (this.modifiers.stop) {
+      handler = stopFilter(handler);
+    }
+    if (this.modifiers.prevent) {
+      handler = preventFilter(handler);
+    }
+    if (this.modifiers.self) {
+      handler = selfFilter(handler);
+    }
+    // key filter
+    var keys = Object.keys(this.modifiers).filter(function (key) {
+      return key !== 'stop' && key !== 'prevent' && key !== 'self' && key !== 'capture';
+    });
+    if (keys.length) {
+      handler = keyFilter(handler, keys);
+    }
+
+    this.reset();
+    this.handler = handler;
+
+    if (this.iframeBind) {
+      this.iframeBind();
+    } else {
+      on(this.el, this.arg, this.handler, this.modifiers.capture);
+    }
+  },
+
+  reset: function reset() {
+    var el = this.iframeBind ? this.el.contentWindow : this.el;
+    if (this.handler) {
+      off(el, this.arg, this.handler);
+    }
+  },
+
+  unbind: function unbind() {
+    this.reset();
+  }
+};
+
+var prefixes = ['-webkit-', '-moz-', '-ms-'];
+var camelPrefixes = ['Webkit', 'Moz', 'ms'];
+var importantRE = /!important;?$/;
+var propCache = Object.create(null);
+
+var testEl = null;
+
+var style = {
+
+  deep: true,
+
+  update: function update(value) {
+    if (typeof value === 'string') {
+      this.el.style.cssText = value;
+    } else if (isArray(value)) {
+      this.handleObject(value.reduce(extend, {}));
+    } else {
+      this.handleObject(value || {});
+    }
+  },
+
+  handleObject: function handleObject(value) {
+    // cache object styles so that only changed props
+    // are actually updated.
+    var cache = this.cache || (this.cache = {});
+    var name, val;
+    for (name in cache) {
+      if (!(name in value)) {
+        this.handleSingle(name, null);
+        delete cache[name];
+      }
+    }
+    for (name in value) {
+      val = value[name];
+      if (val !== cache[name]) {
+        cache[name] = val;
+        this.handleSingle(name, val);
+      }
+    }
+  },
+
+  handleSingle: function handleSingle(prop, value) {
+    prop = normalize(prop);
+    if (!prop) return; // unsupported prop
+    // cast possible numbers/booleans into strings
+    if (value != null) value += '';
+    if (value) {
+      var isImportant = importantRE.test(value) ? 'important' : '';
+      if (isImportant) {
+        /* istanbul ignore if */
+        if (process.env.NODE_ENV !== 'production') {
+          warn('It\'s probably a bad idea to use !important with inline rules. ' + 'This feature will be deprecated in a future version of Vue.');
+        }
+        value = value.replace(importantRE, '').trim();
+        this.el.style.setProperty(prop.kebab, value, isImportant);
+      } else {
+        this.el.style[prop.camel] = value;
+      }
+    } else {
+      this.el.style[prop.camel] = '';
+    }
+  }
+
+};
+
+/**
+ * Normalize a CSS property name.
+ * - cache result
+ * - auto prefix
+ * - camelCase -> dash-case
+ *
+ * @param {String} prop
+ * @return {String}
+ */
+
+function normalize(prop) {
+  if (propCache[prop]) {
+    return propCache[prop];
+  }
+  var res = prefix(prop);
+  propCache[prop] = propCache[res] = res;
+  return res;
+}
+
+/**
+ * Auto detect the appropriate prefix for a CSS property.
+ * https://gist.github.com/paulirish/523692
+ *
+ * @param {String} prop
+ * @return {String}
+ */
+
+function prefix(prop) {
+  prop = hyphenate(prop);
+  var camel = camelize(prop);
+  var upper = camel.charAt(0).toUpperCase() + camel.slice(1);
+  if (!testEl) {
+    testEl = document.createElement('div');
+  }
+  var i = prefixes.length;
+  var prefixed;
+  if (camel !== 'filter' && camel in testEl.style) {
+    return {
+      kebab: prop,
+      camel: camel
+    };
+  }
+  while (i--) {
+    prefixed = camelPrefixes[i] + upper;
+    if (prefixed in testEl.style) {
+      return {
+        kebab: prefixes[i] + prop,
+        camel: prefixed
+      };
+    }
+  }
+}
+
+// xlink
+var xlinkNS = 'http://www.w3.org/1999/xlink';
+var xlinkRE = /^xlink:/;
+
+// check for attributes that prohibit interpolations
+var disallowedInterpAttrRE = /^v-|^:|^@|^(?:is|transition|transition-mode|debounce|track-by|stagger|enter-stagger|leave-stagger)$/;
+// these attributes should also set their corresponding properties
+// because they only affect the initial state of the element
+var attrWithPropsRE = /^(?:value|checked|selected|muted)$/;
+// these attributes expect enumrated values of "true" or "false"
+// but are not boolean attributes
+var enumeratedAttrRE = /^(?:draggable|contenteditable|spellcheck)$/;
+
+// these attributes should set a hidden property for
+// binding v-model to object values
+var modelProps = {
+  value: '_value',
+  'true-value': '_trueValue',
+  'false-value': '_falseValue'
+};
+
+var bind$1 = {
+
+  priority: BIND,
+
+  bind: function bind() {
+    var attr = this.arg;
+    var tag = this.el.tagName;
+    // should be deep watch on object mode
+    if (!attr) {
+      this.deep = true;
+    }
+    // handle interpolation bindings
+    var descriptor = this.descriptor;
+    var tokens = descriptor.interp;
+    if (tokens) {
+      // handle interpolations with one-time tokens
+      if (descriptor.hasOneTime) {
+        this.expression = tokensToExp(tokens, this._scope || this.vm);
+      }
+
+      // only allow binding on native attributes
+      if (disallowedInterpAttrRE.test(attr) || attr === 'name' && (tag === 'PARTIAL' || tag === 'SLOT')) {
+        process.env.NODE_ENV !== 'production' && warn(attr + '="' + descriptor.raw + '": ' + 'attribute interpolation is not allowed in Vue.js ' + 'directives and special attributes.', this.vm);
+        this.el.removeAttribute(attr);
+        this.invalid = true;
+      }
+
+      /* istanbul ignore if */
+      if (process.env.NODE_ENV !== 'production') {
+        var raw = attr + '="' + descriptor.raw + '": ';
+        // warn src
+        if (attr === 'src') {
+          warn(raw + 'interpolation in "src" attribute will cause ' + 'a 404 request. Use v-bind:src instead.', this.vm);
+        }
+
+        // warn style
+        if (attr === 'style') {
+          warn(raw + 'interpolation in "style" attribute will cause ' + 'the attribute to be discarded in Internet Explorer. ' + 'Use v-bind:style instead.', this.vm);
+        }
+      }
+    }
+  },
+
+  update: function update(value) {
+    if (this.invalid) {
+      return;
+    }
+    var attr = this.arg;
+    if (this.arg) {
+      this.handleSingle(attr, value);
+    } else {
+      this.handleObject(value || {});
+    }
+  },
+
+  // share object handler with v-bind:class
+  handleObject: style.handleObject,
+
+  handleSingle: function handleSingle(attr, value) {
+    var el = this.el;
+    var interp = this.descriptor.interp;
+    if (this.modifiers.camel) {
+      attr = camelize(attr);
+    }
+    if (!interp && attrWithPropsRE.test(attr) && attr in el) {
+      var attrValue = attr === 'value' ? value == null // IE9 will set input.value to "null" for null...
+      ? '' : value : value;
+
+      if (el[attr] !== attrValue) {
+        el[attr] = attrValue;
+      }
+    }
+    // set model props
+    var modelProp = modelProps[attr];
+    if (!interp && modelProp) {
+      el[modelProp] = value;
+      // update v-model if present
+      var model = el.__v_model;
+      if (model) {
+        model.listener();
+      }
+    }
+    // do not set value attribute for textarea
+    if (attr === 'value' && el.tagName === 'TEXTAREA') {
+      el.removeAttribute(attr);
+      return;
+    }
+    // update attribute
+    if (enumeratedAttrRE.test(attr)) {
+      el.setAttribute(attr, value ? 'true' : 'false');
+    } else if (value != null && value !== false) {
+      if (attr === 'class') {
+        // handle edge case #1960:
+        // class interpolation should not overwrite Vue transition class
+        if (el.__v_trans) {
+          value += ' ' + el.__v_trans.id + '-transition';
+        }
+        setClass(el, value);
+      } else if (xlinkRE.test(attr)) {
+        el.setAttributeNS(xlinkNS, attr, value === true ? '' : value);
+      } else {
+        el.setAttribute(attr, value === true ? '' : value);
+      }
+    } else {
+      el.removeAttribute(attr);
+    }
+  }
+};
+
+var el = {
+
+  priority: EL,
+
+  bind: function bind() {
+    /* istanbul ignore if */
+    if (!this.arg) {
+      return;
+    }
+    var id = this.id = camelize(this.arg);
+    var refs = (this._scope || this.vm).$els;
+    if (hasOwn(refs, id)) {
+      refs[id] = this.el;
+    } else {
+      defineReactive(refs, id, this.el);
+    }
+  },
+
+  unbind: function unbind() {
+    var refs = (this._scope || this.vm).$els;
+    if (refs[this.id] === this.el) {
+      refs[this.id] = null;
+    }
+  }
+};
+
+var ref = {
+  bind: function bind() {
+    process.env.NODE_ENV !== 'production' && warn('v-ref:' + this.arg + ' must be used on a child ' + 'component. Found on <' + this.el.tagName.toLowerCase() + '>.', this.vm);
+  }
+};
+
+var cloak = {
+  bind: function bind() {
+    var el = this.el;
+    this.vm.$once('pre-hook:compiled', function () {
+      el.removeAttribute('v-cloak');
+    });
+  }
+};
+
+// logic control
+// two-way binding
+// event handling
+// attributes
+// ref & el
+// cloak
+// must export plain object
+var directives = {
+  text: text$1,
+  html: html,
+  'for': vFor,
+  'if': vIf,
+  show: show,
+  model: model,
+  on: on$1,
+  bind: bind$1,
+  el: el,
+  ref: ref,
+  cloak: cloak
+};
+
+var vClass = {
+
+  deep: true,
+
+  update: function update(value) {
+    if (!value) {
+      this.cleanup();
+    } else if (typeof value === 'string') {
+      this.setClass(value.trim().split(/\s+/));
+    } else {
+      this.setClass(normalize$1(value));
+    }
+  },
+
+  setClass: function setClass(value) {
+    this.cleanup(value);
+    for (var i = 0, l = value.length; i < l; i++) {
+      var val = value[i];
+      if (val) {
+        apply(this.el, val, addClass);
+      }
+    }
+    this.prevKeys = value;
+  },
+
+  cleanup: function cleanup(value) {
+    var prevKeys = this.prevKeys;
+    if (!prevKeys) return;
+    var i = prevKeys.length;
+    while (i--) {
+      var key = prevKeys[i];
+      if (!value || value.indexOf(key) < 0) {
+        apply(this.el, key, removeClass);
+      }
+    }
+  }
+};
+
+/**
+ * Normalize objects and arrays (potentially containing objects)
+ * into array of strings.
+ *
+ * @param {Object|Array<String|Object>} value
+ * @return {Array<String>}
+ */
+
+function normalize$1(value) {
+  var res = [];
+  if (isArray(value)) {
+    for (var i = 0, l = value.length; i < l; i++) {
+      var _key = value[i];
+      if (_key) {
+        if (typeof _key === 'string') {
+          res.push(_key);
+        } else {
+          for (var k in _key) {
+            if (_key[k]) res.push(k);
+          }
+        }
+      }
+    }
+  } else if (isObject(value)) {
+    for (var key in value) {
+      if (value[key]) res.push(key);
+    }
+  }
+  return res;
+}
+
+/**
+ * Add or remove a class/classes on an element
+ *
+ * @param {Element} el
+ * @param {String} key The class name. This may or may not
+ *                     contain a space character, in such a
+ *                     case we'll deal with multiple class
+ *                     names at once.
+ * @param {Function} fn
+ */
+
+function apply(el, key, fn) {
+  key = key.trim();
+  if (key.indexOf(' ') === -1) {
+    fn(el, key);
+    return;
+  }
+  // The key contains one or more space characters.
+  // Since a class name doesn't accept such characters, we
+  // treat it as multiple classes.
+  var keys = key.split(/\s+/);
+  for (var i = 0, l = keys.length; i < l; i++) {
+    fn(el, keys[i]);
+  }
+}
+
+var component = {
+
+  priority: COMPONENT,
+
+  params: ['keep-alive', 'transition-mode', 'inline-template'],
+
+  /**
+   * Setup. Two possible usages:
+   *
+   * - static:
+   *   <comp> or <div v-component="comp">
+   *
+   * - dynamic:
+   *   <component :is="view">
+   */
+
+  bind: function bind() {
+    if (!this.el.__vue__) {
+      // keep-alive cache
+      this.keepAlive = this.params.keepAlive;
+      if (this.keepAlive) {
+        this.cache = {};
+      }
+      // check inline-template
+      if (this.params.inlineTemplate) {
+        // extract inline template as a DocumentFragment
+        this.inlineTemplate = extractContent(this.el, true);
+      }
+      // component resolution related state
+      this.pendingComponentCb = this.Component = null;
+      // transition related state
+      this.pendingRemovals = 0;
+      this.pendingRemovalCb = null;
+      // create a ref anchor
+      this.anchor = createAnchor('v-component');
+      replace(this.el, this.anchor);
+      // remove is attribute.
+      // this is removed during compilation, but because compilation is
+      // cached, when the component is used elsewhere this attribute
+      // will remain at link time.
+      this.el.removeAttribute('is');
+      this.el.removeAttribute(':is');
+      // remove ref, same as above
+      if (this.descriptor.ref) {
+        this.el.removeAttribute('v-ref:' + hyphenate(this.descriptor.ref));
+      }
+      // if static, build right now.
+      if (this.literal) {
+        this.setComponent(this.expression);
+      }
+    } else {
+      process.env.NODE_ENV !== 'production' && warn('cannot mount component "' + this.expression + '" ' + 'on already mounted element: ' + this.el);
+    }
+  },
+
+  /**
+   * Public update, called by the watcher in the dynamic
+   * literal scenario, e.g. <component :is="view">
+   */
+
+  update: function update(value) {
+    if (!this.literal) {
+      this.setComponent(value);
+    }
+  },
+
+  /**
+   * Switch dynamic components. May resolve the component
+   * asynchronously, and perform transition based on
+   * specified transition mode. Accepts a few additional
+   * arguments specifically for vue-router.
+   *
+   * The callback is called when the full transition is
+   * finished.
+   *
+   * @param {String} value
+   * @param {Function} [cb]
+   */
+
+  setComponent: function setComponent(value, cb) {
+    this.invalidatePending();
+    if (!value) {
+      // just remove current
+      this.unbuild(true);
+      this.remove(this.childVM, cb);
+      this.childVM = null;
+    } else {
+      var self = this;
+      this.resolveComponent(value, function () {
+        self.mountComponent(cb);
+      });
+    }
+  },
+
+  /**
+   * Resolve the component constructor to use when creating
+   * the child vm.
+   *
+   * @param {String|Function} value
+   * @param {Function} cb
+   */
+
+  resolveComponent: function resolveComponent(value, cb) {
+    var self = this;
+    this.pendingComponentCb = cancellable(function (Component) {
+      self.ComponentName = Component.options.name || (typeof value === 'string' ? value : null);
+      self.Component = Component;
+      cb();
+    });
+    this.vm._resolveComponent(value, this.pendingComponentCb);
+  },
+
+  /**
+   * Create a new instance using the current constructor and
+   * replace the existing instance. This method doesn't care
+   * whether the new component and the old one are actually
+   * the same.
+   *
+   * @param {Function} [cb]
+   */
+
+  mountComponent: function mountComponent(cb) {
+    // actual mount
+    this.unbuild(true);
+    var self = this;
+    var activateHooks = this.Component.options.activate;
+    var cached = this.getCached();
+    var newComponent = this.build();
+    if (activateHooks && !cached) {
+      this.waitingFor = newComponent;
+      callActivateHooks(activateHooks, newComponent, function () {
+        if (self.waitingFor !== newComponent) {
+          return;
+        }
+        self.waitingFor = null;
+        self.transition(newComponent, cb);
+      });
+    } else {
+      // update ref for kept-alive component
+      if (cached) {
+        newComponent._updateRef();
+      }
+      this.transition(newComponent, cb);
+    }
+  },
+
+  /**
+   * When the component changes or unbinds before an async
+   * constructor is resolved, we need to invalidate its
+   * pending callback.
+   */
+
+  invalidatePending: function invalidatePending() {
+    if (this.pendingComponentCb) {
+      this.pendingComponentCb.cancel();
+      this.pendingComponentCb = null;
+    }
+  },
+
+  /**
+   * Instantiate/insert a new child vm.
+   * If keep alive and has cached instance, insert that
+   * instance; otherwise build a new one and cache it.
+   *
+   * @param {Object} [extraOptions]
+   * @return {Vue} - the created instance
+   */
+
+  build: function build(extraOptions) {
+    var cached = this.getCached();
+    if (cached) {
+      return cached;
+    }
+    if (this.Component) {
+      // default options
+      var options = {
+        name: this.ComponentName,
+        el: cloneNode(this.el),
+        template: this.inlineTemplate,
+        // make sure to add the child with correct parent
+        // if this is a transcluded component, its parent
+        // should be the transclusion host.
+        parent: this._host || this.vm,
+        // if no inline-template, then the compiled
+        // linker can be cached for better performance.
+        _linkerCachable: !this.inlineTemplate,
+        _ref: this.descriptor.ref,
+        _asComponent: true,
+        _isRouterView: this._isRouterView,
+        // if this is a transcluded component, context
+        // will be the common parent vm of this instance
+        // and its host.
+        _context: this.vm,
+        // if this is inside an inline v-for, the scope
+        // will be the intermediate scope created for this
+        // repeat fragment. this is used for linking props
+        // and container directives.
+        _scope: this._scope,
+        // pass in the owner fragment of this component.
+        // this is necessary so that the fragment can keep
+        // track of its contained components in order to
+        // call attach/detach hooks for them.
+        _frag: this._frag
+      };
+      // extra options
+      // in 1.0.0 this is used by vue-router only
+      /* istanbul ignore if */
+      if (extraOptions) {
+        extend(options, extraOptions);
+      }
+      var child = new this.Component(options);
+      if (this.keepAlive) {
+        this.cache[this.Component.cid] = child;
+      }
+      /* istanbul ignore if */
+      if (process.env.NODE_ENV !== 'production' && this.el.hasAttribute('transition') && child._isFragment) {
+        warn('Transitions will not work on a fragment instance. ' + 'Template: ' + child.$options.template, child);
+      }
+      return child;
+    }
+  },
+
+  /**
+   * Try to get a cached instance of the current component.
+   *
+   * @return {Vue|undefined}
+   */
+
+  getCached: function getCached() {
+    return this.keepAlive && this.cache[this.Component.cid];
+  },
+
+  /**
+   * Teardown the current child, but defers cleanup so
+   * that we can separate the destroy and removal steps.
+   *
+   * @param {Boolean} defer
+   */
+
+  unbuild: function unbuild(defer) {
+    if (this.waitingFor) {
+      if (!this.keepAlive) {
+        this.waitingFor.$destroy();
+      }
+      this.waitingFor = null;
+    }
+    var child = this.childVM;
+    if (!child || this.keepAlive) {
+      if (child) {
+        // remove ref
+        child._inactive = true;
+        child._updateRef(true);
+      }
+      return;
+    }
+    // the sole purpose of `deferCleanup` is so that we can
+    // "deactivate" the vm right now and perform DOM removal
+    // later.
+    child.$destroy(false, defer);
+  },
+
+  /**
+   * Remove current destroyed child and manually do
+   * the cleanup after removal.
+   *
+   * @param {Function} cb
+   */
+
+  remove: function remove(child, cb) {
+    var keepAlive = this.keepAlive;
+    if (child) {
+      // we may have a component switch when a previous
+      // component is still being transitioned out.
+      // we want to trigger only one lastest insertion cb
+      // when the existing transition finishes. (#1119)
+      this.pendingRemovals++;
+      this.pendingRemovalCb = cb;
+      var self = this;
+      child.$remove(function () {
+        self.pendingRemovals--;
+        if (!keepAlive) child._cleanup();
+        if (!self.pendingRemovals && self.pendingRemovalCb) {
+          self.pendingRemovalCb();
+          self.pendingRemovalCb = null;
+        }
+      });
+    } else if (cb) {
+      cb();
+    }
+  },
+
+  /**
+   * Actually swap the components, depending on the
+   * transition mode. Defaults to simultaneous.
+   *
+   * @param {Vue} target
+   * @param {Function} [cb]
+   */
+
+  transition: function transition(target, cb) {
+    var self = this;
+    var current = this.childVM;
+    // for devtool inspection
+    if (current) current._inactive = true;
+    target._inactive = false;
+    this.childVM = target;
+    switch (self.params.transitionMode) {
+      case 'in-out':
+        target.$before(self.anchor, function () {
+          self.remove(current, cb);
+        });
+        break;
+      case 'out-in':
+        self.remove(current, function () {
+          target.$before(self.anchor, cb);
+        });
+        break;
+      default:
+        self.remove(current);
+        target.$before(self.anchor, cb);
+    }
+  },
+
+  /**
+   * Unbind.
+   */
+
+  unbind: function unbind() {
+    this.invalidatePending();
+    // Do not defer cleanup when unbinding
+    this.unbuild();
+    // destroy all keep-alive cached instances
+    if (this.cache) {
+      for (var key in this.cache) {
+        this.cache[key].$destroy();
+      }
+      this.cache = null;
+    }
+  }
+};
+
+/**
+ * Call activate hooks in order (asynchronous)
+ *
+ * @param {Array} hooks
+ * @param {Vue} vm
+ * @param {Function} cb
+ */
+
+function callActivateHooks(hooks, vm, cb) {
+  var total = hooks.length;
+  var called = 0;
+  hooks[0].call(vm, next);
+  function next() {
+    if (++called >= total) {
+      cb();
+    } else {
+      hooks[called].call(vm, next);
+    }
+  }
+}
+
+var propBindingModes = config._propBindingModes;
+var empty = {};
+
+// regexes
+var identRE$1 = /^[$_a-zA-Z]+[\w$]*$/;
+var settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/;
+
+/**
+ * Compile props on a root element and return
+ * a props link function.
+ *
+ * @param {Element|DocumentFragment} el
+ * @param {Array} propOptions
+ * @param {Vue} vm
+ * @return {Function} propsLinkFn
+ */
+
+function compileProps(el, propOptions, vm) {
+  var props = [];
+  var propsData = vm.$options.propsData;
+  var names = Object.keys(propOptions);
+  var i = names.length;
+  var options, name, attr, value, path, parsed, prop;
+  while (i--) {
+    name = names[i];
+    options = propOptions[name] || empty;
+
+    if (process.env.NODE_ENV !== 'production' && name === '$data') {
+      warn('Do not use $data as prop.', vm);
+      continue;
+    }
+
+    // props could contain dashes, which will be
+    // interpreted as minus calculations by the parser
+    // so we need to camelize the path here
+    path = camelize(name);
+    if (!identRE$1.test(path)) {
+      process.env.NODE_ENV !== 'production' && warn('Invalid prop key: "' + name + '". Prop keys ' + 'must be valid identifiers.', vm);
+      continue;
+    }
+
+    prop = {
+      name: name,
+      path: path,
+      options: options,
+      mode: propBindingModes.ONE_WAY,
+      raw: null
+    };
+
+    attr = hyphenate(name);
+    // first check dynamic version
+    if ((value = getBindAttr(el, attr)) === null) {
+      if ((value = getBindAttr(el, attr + '.sync')) !== null) {
+        prop.mode = propBindingModes.TWO_WAY;
+      } else if ((value = getBindAttr(el, attr + '.once')) !== null) {
+        prop.mode = propBindingModes.ONE_TIME;
+      }
+    }
+    if (value !== null) {
+      // has dynamic binding!
+      prop.raw = value;
+      parsed = parseDirective(value);
+      value = parsed.expression;
+      prop.filters = parsed.filters;
+      // check binding type
+      if (isLiteral(value) && !parsed.filters) {
+        // for expressions containing literal numbers and
+        // booleans, there's no need to setup a prop binding,
+        // so we can optimize them as a one-time set.
+        prop.optimizedLiteral = true;
+      } else {
+        prop.dynamic = true;
+        // check non-settable path for two-way bindings
+        if (process.env.NODE_ENV !== 'production' && prop.mode === propBindingModes.TWO_WAY && !settablePathRE.test(value)) {
+          prop.mode = propBindingModes.ONE_WAY;
+          warn('Cannot bind two-way prop with non-settable ' + 'parent path: ' + value, vm);
+        }
+      }
+      prop.parentPath = value;
+
+      // warn required two-way
+      if (process.env.NODE_ENV !== 'production' && options.twoWay && prop.mode !== propBindingModes.TWO_WAY) {
+        warn('Prop "' + name + '" expects a two-way binding type.', vm);
+      }
+    } else if ((value = getAttr(el, attr)) !== null) {
+      // has literal binding!
+      prop.raw = value;
+    } else if (propsData && (value = propsData[name] || propsData[path]) !== null) {
+      // has propsData
+      prop.raw = value;
+    } else if (process.env.NODE_ENV !== 'production') {
+      // check possible camelCase prop usage
+      var lowerCaseName = path.toLowerCase();
+      value = /[A-Z\-]/.test(name) && (el.getAttribute(lowerCaseName) || el.getAttribute(':' + lowerCaseName) || el.getAttribute('v-bind:' + lowerCaseName) || el.getAttribute(':' + lowerCaseName + '.once') || el.getAttribute('v-bind:' + lowerCaseName + '.once') || el.getAttribute(':' + lowerCaseName + '.sync') || el.getAttribute('v-bind:' + lowerCaseName + '.sync'));
+      if (value) {
+        warn('Possible usage error for prop `' + lowerCaseName + '` - ' + 'did you mean `' + attr + '`? HTML is case-insensitive, remember to use ' + 'kebab-case for props in templates.', vm);
+      } else if (options.required && (!propsData || !(name in propsData) && !(path in propsData))) {
+        // warn missing required
+        warn('Missing required prop: ' + name, vm);
+      }
+    }
+    // push prop
+    props.push(prop);
+  }
+  return makePropsLinkFn(props);
+}
+
+/**
+ * Build a function that applies props to a vm.
+ *
+ * @param {Array} props
+ * @return {Function} propsLinkFn
+ */
+
+function makePropsLinkFn(props) {
+  return function propsLinkFn(vm, scope) {
+    // store resolved props info
+    vm._props = {};
+    var inlineProps = vm.$options.propsData;
+    var i = props.length;
+    var prop, path, options, value, raw;
+    while (i--) {
+      prop = props[i];
+      raw = prop.raw;
+      path = prop.path;
+      options = prop.options;
+      vm._props[path] = prop;
+      if (inlineProps && hasOwn(inlineProps, path)) {
+        initProp(vm, prop, inlineProps[path]);
+      }if (raw === null) {
+        // initialize absent prop
+        initProp(vm, prop, undefined);
+      } else if (prop.dynamic) {
+        // dynamic prop
+        if (prop.mode === propBindingModes.ONE_TIME) {
+          // one time binding
+          value = (scope || vm._context || vm).$get(prop.parentPath);
+          initProp(vm, prop, value);
+        } else {
+          if (vm._context) {
+            // dynamic binding
+            vm._bindDir({
+              name: 'prop',
+              def: propDef,
+              prop: prop
+            }, null, null, scope); // el, host, scope
+          } else {
+              // root instance
+              initProp(vm, prop, vm.$get(prop.parentPath));
+            }
+        }
+      } else if (prop.optimizedLiteral) {
+        // optimized literal, cast it and just set once
+        var stripped = stripQuotes(raw);
+        value = stripped === raw ? toBoolean(toNumber(raw)) : stripped;
+        initProp(vm, prop, value);
+      } else {
+        // string literal, but we need to cater for
+        // Boolean props with no value, or with same
+        // literal value (e.g. disabled="disabled")
+        // see https://github.com/vuejs/vue-loader/issues/182
+        value = options.type === Boolean && (raw === '' || raw === hyphenate(prop.name)) ? true : raw;
+        initProp(vm, prop, value);
+      }
+    }
+  };
+}
+
+/**
+ * Process a prop with a rawValue, applying necessary coersions,
+ * default values & assertions and call the given callback with
+ * processed value.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @param {*} rawValue
+ * @param {Function} fn
+ */
+
+function processPropValue(vm, prop, rawValue, fn) {
+  var isSimple = prop.dynamic && isSimplePath(prop.parentPath);
+  var value = rawValue;
+  if (value === undefined) {
+    value = getPropDefaultValue(vm, prop);
+  }
+  value = coerceProp(prop, value, vm);
+  var coerced = value !== rawValue;
+  if (!assertProp(prop, value, vm)) {
+    value = undefined;
+  }
+  if (isSimple && !coerced) {
+    withoutConversion(function () {
+      fn(value);
+    });
+  } else {
+    fn(value);
+  }
+}
+
+/**
+ * Set a prop's initial value on a vm and its data object.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @param {*} value
+ */
+
+function initProp(vm, prop, value) {
+  processPropValue(vm, prop, value, function (value) {
+    defineReactive(vm, prop.path, value);
+  });
+}
+
+/**
+ * Update a prop's value on a vm.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @param {*} value
+ */
+
+function updateProp(vm, prop, value) {
+  processPropValue(vm, prop, value, function (value) {
+    vm[prop.path] = value;
+  });
+}
+
+/**
+ * Get the default value of a prop.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @return {*}
+ */
+
+function getPropDefaultValue(vm, prop) {
+  // no default, return undefined
+  var options = prop.options;
+  if (!hasOwn(options, 'default')) {
+    // absent boolean value defaults to false
+    return options.type === Boolean ? false : undefined;
+  }
+  var def = options['default'];
+  // warn against non-factory defaults for Object & Array
+  if (isObject(def)) {
+    process.env.NODE_ENV !== 'production' && warn('Invalid default value for prop "' + prop.name + '": ' + 'Props with type Object/Array must use a factory function ' + 'to return the default value.', vm);
+  }
+  // call factory function for non-Function types
+  return typeof def === 'function' && options.type !== Function ? def.call(vm) : def;
+}
+
+/**
+ * Assert whether a prop is valid.
+ *
+ * @param {Object} prop
+ * @param {*} value
+ * @param {Vue} vm
+ */
+
+function assertProp(prop, value, vm) {
+  if (!prop.options.required && ( // non-required
+  prop.raw === null || // abscent
+  value == null) // null or undefined
+  ) {
+      return true;
+    }
+  var options = prop.options;
+  var type = options.type;
+  var valid = !type;
+  var expectedTypes = [];
+  if (type) {
+    if (!isArray(type)) {
+      type = [type];
+    }
+    for (var i = 0; i < type.length && !valid; i++) {
+      var assertedType = assertType(value, type[i]);
+      expectedTypes.push(assertedType.expectedType);
+      valid = assertedType.valid;
+    }
+  }
+  if (!valid) {
+    if (process.env.NODE_ENV !== 'production') {
+      warn('Invalid prop: type check failed for prop "' + prop.name + '".' + ' Expected ' + expectedTypes.map(formatType).join(', ') + ', got ' + formatValue(value) + '.', vm);
+    }
+    return false;
+  }
+  var validator = options.validator;
+  if (validator) {
+    if (!validator(value)) {
+      process.env.NODE_ENV !== 'production' && warn('Invalid prop: custom validator check failed for prop "' + prop.name + '".', vm);
+      return false;
+    }
+  }
+  return true;
+}
+
+/**
+ * Force parsing value with coerce option.
+ *
+ * @param {*} value
+ * @param {Object} options
+ * @return {*}
+ */
+
+function coerceProp(prop, value, vm) {
+  var coerce = prop.options.coerce;
+  if (!coerce) {
+    return value;
+  }
+  if (typeof coerce === 'function') {
+    return coerce(value);
+  } else {
+    process.env.NODE_ENV !== 'production' && warn('Invalid coerce for prop "' + prop.name + '": expected function, got ' + typeof coerce + '.', vm);
+    return value;
+  }
+}
+
+/**
+ * Assert the type of a value
+ *
+ * @param {*} value
+ * @param {Function} type
+ * @return {Object}
+ */
+
+function assertType(value, type) {
+  var valid;
+  var expectedType;
+  if (type === String) {
+    expectedType = 'string';
+    valid = typeof value === expectedType;
+  } else if (type === Number) {
+    expectedType = 'number';
+    valid = typeof value === expectedType;
+  } else if (type === Boolean) {
+    expectedType = 'boolean';
+    valid = typeof value === expectedType;
+  } else if (type === Function) {
+    expectedType = 'function';
+    valid = typeof value === expectedType;
+  } else if (type === Object) {
+    expectedType = 'object';
+    valid = isPlainObject(value);
+  } else if (type === Array) {
+    expectedType = 'array';
+    valid = isArray(value);
+  } else {
+    valid = value instanceof type;
+  }
+  return {
+    valid: valid,
+    expectedType: expectedType
+  };
+}
+
+/**
+ * Format type for output
+ *
+ * @param {String} type
+ * @return {String}
+ */
+
+function formatType(type) {
+  return type ? type.charAt(0).toUpperCase() + type.slice(1) : 'custom type';
+}
+
+/**
+ * Format value
+ *
+ * @param {*} value
+ * @return {String}
+ */
+
+function formatValue(val) {
+  return Object.prototype.toString.call(val).slice(8, -1);
+}
+
+var bindingModes = config._propBindingModes;
+
+var propDef = {
+
+  bind: function bind() {
+    var child = this.vm;
+    var parent = child._context;
+    // passed in from compiler directly
+    var prop = this.descriptor.prop;
+    var childKey = prop.path;
+    var parentKey = prop.parentPath;
+    var twoWay = prop.mode === bindingModes.TWO_WAY;
+
+    var parentWatcher = this.parentWatcher = new Watcher(parent, parentKey, function (val) {
+      updateProp(child, prop, val);
+    }, {
+      twoWay: twoWay,
+      filters: prop.filters,
+      // important: props need to be observed on the
+      // v-for scope if present
+      scope: this._scope
+    });
+
+    // set the child initial value.
+    initProp(child, prop, parentWatcher.value);
+
+    // setup two-way binding
+    if (twoWay) {
+      // important: defer the child watcher creation until
+      // the created hook (after data observation)
+      var self = this;
+      child.$once('pre-hook:created', function () {
+        self.childWatcher = new Watcher(child, childKey, function (val) {
+          parentWatcher.set(val);
+        }, {
+          // ensure sync upward before parent sync down.
+          // this is necessary in cases e.g. the child
+          // mutates a prop array, then replaces it. (#1683)
+          sync: true
+        });
+      });
+    }
+  },
+
+  unbind: function unbind() {
+    this.parentWatcher.teardown();
+    if (this.childWatcher) {
+      this.childWatcher.teardown();
+    }
+  }
+};
+
+var queue$1 = [];
+var queued = false;
+
+/**
+ * Push a job into the queue.
+ *
+ * @param {Function} job
+ */
+
+function pushJob(job) {
+  queue$1.push(job);
+  if (!queued) {
+    queued = true;
+    nextTick(flush);
+  }
+}
+
+/**
+ * Flush the queue, and do one forced reflow before
+ * triggering transitions.
+ */
+
+function flush() {
+  // Force layout
+  var f = document.documentElement.offsetHeight;
+  for (var i = 0; i < queue$1.length; i++) {
+    queue$1[i]();
+  }
+  queue$1 = [];
+  queued = false;
+  // dummy return, so js linters don't complain about
+  // unused variable f
+  return f;
+}
+
+var TYPE_TRANSITION = 'transition';
+var TYPE_ANIMATION = 'animation';
+var transDurationProp = transitionProp + 'Duration';
+var animDurationProp = animationProp + 'Duration';
+
+/**
+ * If a just-entered element is applied the
+ * leave class while its enter transition hasn't started yet,
+ * and the transitioned property has the same value for both
+ * enter/leave, then the leave transition will be skipped and
+ * the transitionend event never fires. This function ensures
+ * its callback to be called after a transition has started
+ * by waiting for double raf.
+ *
+ * It falls back to setTimeout on devices that support CSS
+ * transitions but not raf (e.g. Android 4.2 browser) - since
+ * these environments are usually slow, we are giving it a
+ * relatively large timeout.
+ */
+
+var raf = inBrowser && window.requestAnimationFrame;
+var waitForTransitionStart = raf
+/* istanbul ignore next */
+? function (fn) {
+  raf(function () {
+    raf(fn);
+  });
+} : function (fn) {
+  setTimeout(fn, 50);
+};
+
+/**
+ * A Transition object that encapsulates the state and logic
+ * of the transition.
+ *
+ * @param {Element} el
+ * @param {String} id
+ * @param {Object} hooks
+ * @param {Vue} vm
+ */
+function Transition(el, id, hooks, vm) {
+  this.id = id;
+  this.el = el;
+  this.enterClass = hooks && hooks.enterClass || id + '-enter';
+  this.leaveClass = hooks && hooks.leaveClass || id + '-leave';
+  this.hooks = hooks;
+  this.vm = vm;
+  // async state
+  this.pendingCssEvent = this.pendingCssCb = this.cancel = this.pendingJsCb = this.op = this.cb = null;
+  this.justEntered = false;
+  this.entered = this.left = false;
+  this.typeCache = {};
+  // check css transition type
+  this.type = hooks && hooks.type;
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV !== 'production') {
+    if (this.type && this.type !== TYPE_TRANSITION && this.type !== TYPE_ANIMATION) {
+      warn('invalid CSS transition type for transition="' + this.id + '": ' + this.type, vm);
+    }
+  }
+  // bind
+  var self = this;['enterNextTick', 'enterDone', 'leaveNextTick', 'leaveDone'].forEach(function (m) {
+    self[m] = bind(self[m], self);
+  });
+}
+
+var p$1 = Transition.prototype;
+
+/**
+ * Start an entering transition.
+ *
+ * 1. enter transition triggered
+ * 2. call beforeEnter hook
+ * 3. add enter class
+ * 4. insert/show element
+ * 5. call enter hook (with possible explicit js callback)
+ * 6. reflow
+ * 7. based on transition type:
+ *    - transition:
+ *        remove class now, wait for transitionend,
+ *        then done if there's no explicit js callback.
+ *    - animation:
+ *        wait for animationend, remove class,
+ *        then done if there's no explicit js callback.
+ *    - no css transition:
+ *        done now if there's no explicit js callback.
+ * 8. wait for either done or js callback, then call
+ *    afterEnter hook.
+ *
+ * @param {Function} op - insert/show the element
+ * @param {Function} [cb]
+ */
+
+p$1.enter = function (op, cb) {
+  this.cancelPending();
+  this.callHook('beforeEnter');
+  this.cb = cb;
+  addClass(this.el, this.enterClass);
+  op();
+  this.entered = false;
+  this.callHookWithCb('enter');
+  if (this.entered) {
+    return; // user called done synchronously.
+  }
+  this.cancel = this.hooks && this.hooks.enterCancelled;
+  pushJob(this.enterNextTick);
+};
+
+/**
+ * The "nextTick" phase of an entering transition, which is
+ * to be pushed into a queue and executed after a reflow so
+ * that removing the class can trigger a CSS transition.
+ */
+
+p$1.enterNextTick = function () {
+  var _this = this;
+
+  // prevent transition skipping
+  this.justEntered = true;
+  waitForTransitionStart(function () {
+    _this.justEntered = false;
+  });
+  var enterDone = this.enterDone;
+  var type = this.getCssTransitionType(this.enterClass);
+  if (!this.pendingJsCb) {
+    if (type === TYPE_TRANSITION) {
+      // trigger transition by removing enter class now
+      removeClass(this.el, this.enterClass);
+      this.setupCssCb(transitionEndEvent, enterDone);
+    } else if (type === TYPE_ANIMATION) {
+      this.setupCssCb(animationEndEvent, enterDone);
+    } else {
+      enterDone();
+    }
+  } else if (type === TYPE_TRANSITION) {
+    removeClass(this.el, this.enterClass);
+  }
+};
+
+/**
+ * The "cleanup" phase of an entering transition.
+ */
+
+p$1.enterDone = function () {
+  this.entered = true;
+  this.cancel = this.pendingJsCb = null;
+  removeClass(this.el, this.enterClass);
+  this.callHook('afterEnter');
+  if (this.cb) this.cb();
+};
+
+/**
+ * Start a leaving transition.
+ *
+ * 1. leave transition triggered.
+ * 2. call beforeLeave hook
+ * 3. add leave class (trigger css transition)
+ * 4. call leave hook (with possible explicit js callback)
+ * 5. reflow if no explicit js callback is provided
+ * 6. based on transition type:
+ *    - transition or animation:
+ *        wait for end event, remove class, then done if
+ *        there's no explicit js callback.
+ *    - no css transition:
+ *        done if there's no explicit js callback.
+ * 7. wait for either done or js callback, then call
+ *    afterLeave hook.
+ *
+ * @param {Function} op - remove/hide the element
+ * @param {Function} [cb]
+ */
+
+p$1.leave = function (op, cb) {
+  this.cancelPending();
+  this.callHook('beforeLeave');
+  this.op = op;
+  this.cb = cb;
+  addClass(this.el, this.leaveClass);
+  this.left = false;
+  this.callHookWithCb('leave');
+  if (this.left) {
+    return; // user called done synchronously.
+  }
+  this.cancel = this.hooks && this.hooks.leaveCancelled;
+  // only need to handle leaveDone if
+  // 1. the transition is already done (synchronously called
+  //    by the user, which causes this.op set to null)
+  // 2. there's no explicit js callback
+  if (this.op && !this.pendingJsCb) {
+    // if a CSS transition leaves immediately after enter,
+    // the transitionend event never fires. therefore we
+    // detect such cases and end the leave immediately.
+    if (this.justEntered) {
+      this.leaveDone();
+    } else {
+      pushJob(this.leaveNextTick);
+    }
+  }
+};
+
+/**
+ * The "nextTick" phase of a leaving transition.
+ */
+
+p$1.leaveNextTick = function () {
+  var type = this.getCssTransitionType(this.leaveClass);
+  if (type) {
+    var event = type === TYPE_TRANSITION ? transitionEndEvent : animationEndEvent;
+    this.setupCssCb(event, this.leaveDone);
+  } else {
+    this.leaveDone();
+  }
+};
+
+/**
+ * The "cleanup" phase of a leaving transition.
+ */
+
+p$1.leaveDone = function () {
+  this.left = true;
+  this.cancel = this.pendingJsCb = null;
+  this.op();
+  removeClass(this.el, this.leaveClass);
+  this.callHook('afterLeave');
+  if (this.cb) this.cb();
+  this.op = null;
+};
+
+/**
+ * Cancel any pending callbacks from a previously running
+ * but not finished transition.
+ */
+
+p$1.cancelPending = function () {
+  this.op = this.cb = null;
+  var hasPending = false;
+  if (this.pendingCssCb) {
+    hasPending = true;
+    off(this.el, this.pendingCssEvent, this.pendingCssCb);
+    this.pendingCssEvent = this.pendingCssCb = null;
+  }
+  if (this.pendingJsCb) {
+    hasPending = true;
+    this.pendingJsCb.cancel();
+    this.pendingJsCb = null;
+  }
+  if (hasPending) {
+    removeClass(this.el, this.enterClass);
+    removeClass(this.el, this.leaveClass);
+  }
+  if (this.cancel) {
+    this.cancel.call(this.vm, this.el);
+    this.cancel = null;
+  }
+};
+
+/**
+ * Call a user-provided synchronous hook function.
+ *
+ * @param {String} type
+ */
+
+p$1.callHook = function (type) {
+  if (this.hooks && this.hooks[type]) {
+    this.hooks[type].call(this.vm, this.el);
+  }
+};
+
+/**
+ * Call a user-provided, potentially-async hook function.
+ * We check for the length of arguments to see if the hook
+ * expects a `done` callback. If true, the transition's end
+ * will be determined by when the user calls that callback;
+ * otherwise, the end is determined by the CSS transition or
+ * animation.
+ *
+ * @param {String} type
+ */
+
+p$1.callHookWithCb = function (type) {
+  var hook = this.hooks && this.hooks[type];
+  if (hook) {
+    if (hook.length > 1) {
+      this.pendingJsCb = cancellable(this[type + 'Done']);
+    }
+    hook.call(this.vm, this.el, this.pendingJsCb);
+  }
+};
+
+/**
+ * Get an element's transition type based on the
+ * calculated styles.
+ *
+ * @param {String} className
+ * @return {Number}
+ */
+
+p$1.getCssTransitionType = function (className) {
+  /* istanbul ignore if */
+  if (!transitionEndEvent ||
+  // skip CSS transitions if page is not visible -
+  // this solves the issue of transitionend events not
+  // firing until the page is visible again.
+  // pageVisibility API is supported in IE10+, same as
+  // CSS transitions.
+  document.hidden ||
+  // explicit js-only transition
+  this.hooks && this.hooks.css === false ||
+  // element is hidden
+  isHidden(this.el)) {
+    return;
+  }
+  var type = this.type || this.typeCache[className];
+  if (type) return type;
+  var inlineStyles = this.el.style;
+  var computedStyles = window.getComputedStyle(this.el);
+  var transDuration = inlineStyles[transDurationProp] || computedStyles[transDurationProp];
+  if (transDuration && transDuration !== '0s') {
+    type = TYPE_TRANSITION;
+  } else {
+    var animDuration = inlineStyles[animDurationProp] || computedStyles[animDurationProp];
+    if (animDuration && animDuration !== '0s') {
+      type = TYPE_ANIMATION;
+    }
+  }
+  if (type) {
+    this.typeCache[className] = type;
+  }
+  return type;
+};
+
+/**
+ * Setup a CSS transitionend/animationend callback.
+ *
+ * @param {String} event
+ * @param {Function} cb
+ */
+
+p$1.setupCssCb = function (event, cb) {
+  this.pendingCssEvent = event;
+  var self = this;
+  var el = this.el;
+  var onEnd = this.pendingCssCb = function (e) {
+    if (e.target === el) {
+      off(el, event, onEnd);
+      self.pendingCssEvent = self.pendingCssCb = null;
+      if (!self.pendingJsCb && cb) {
+        cb();
+      }
+    }
+  };
+  on(el, event, onEnd);
+};
+
+/**
+ * Check if an element is hidden - in that case we can just
+ * skip the transition alltogether.
+ *
+ * @param {Element} el
+ * @return {Boolean}
+ */
+
+function isHidden(el) {
+  if (/svg$/.test(el.namespaceURI)) {
+    // SVG elements do not have offset(Width|Height)
+    // so we need to check the client rect
+    var rect = el.getBoundingClientRect();
+    return !(rect.width || rect.height);
+  } else {
+    return !(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
+  }
+}
+
+var transition$1 = {
+
+  priority: TRANSITION,
+
+  update: function update(id, oldId) {
+    var el = this.el;
+    // resolve on owner vm
+    var hooks = resolveAsset(this.vm.$options, 'transitions', id);
+    id = id || 'v';
+    oldId = oldId || 'v';
+    el.__v_trans = new Transition(el, id, hooks, this.vm);
+    removeClass(el, oldId + '-transition');
+    addClass(el, id + '-transition');
+  }
+};
+
+var internalDirectives = {
+  style: style,
+  'class': vClass,
+  component: component,
+  prop: propDef,
+  transition: transition$1
+};
+
+// special binding prefixes
+var bindRE = /^v-bind:|^:/;
+var onRE = /^v-on:|^@/;
+var dirAttrRE = /^v-([^:]+)(?:$|:(.*)$)/;
+var modifierRE = /\.[^\.]+/g;
+var transitionRE = /^(v-bind:|:)?transition$/;
+
+// default directive priority
+var DEFAULT_PRIORITY = 1000;
+var DEFAULT_TERMINAL_PRIORITY = 2000;
+
+/**
+ * Compile a template and return a reusable composite link
+ * function, which recursively contains more link functions
+ * inside. This top level compile function would normally
+ * be called on instance root nodes, but can also be used
+ * for partial compilation if the partial argument is true.
+ *
+ * The returned composite link function, when called, will
+ * return an unlink function that tearsdown all directives
+ * created during the linking phase.
+ *
+ * @param {Element|DocumentFragment} el
+ * @param {Object} options
+ * @param {Boolean} partial
+ * @return {Function}
+ */
+
+function compile(el, options, partial) {
+  // link function for the node itself.
+  var nodeLinkFn = partial || !options._asComponent ? compileNode(el, options) : null;
+  // link function for the childNodes
+  var childLinkFn = !(nodeLinkFn && nodeLinkFn.terminal) && !isScript(el) && el.hasChildNodes() ? compileNodeList(el.childNodes, options) : null;
+
+  /**
+   * A composite linker function to be called on a already
+   * compiled piece of DOM, which instantiates all directive
+   * instances.
+   *
+   * @param {Vue} vm
+   * @param {Element|DocumentFragment} el
+   * @param {Vue} [host] - host vm of transcluded content
+   * @param {Object} [scope] - v-for scope
+   * @param {Fragment} [frag] - link context fragment
+   * @return {Function|undefined}
+   */
+
+  return function compositeLinkFn(vm, el, host, scope, frag) {
+    // cache childNodes before linking parent, fix #657
+    var childNodes = toArray(el.childNodes);
+    // link
+    var dirs = linkAndCapture(function compositeLinkCapturer() {
+      if (nodeLinkFn) nodeLinkFn(vm, el, host, scope, frag);
+      if (childLinkFn) childLinkFn(vm, childNodes, host, scope, frag);
+    }, vm);
+    return makeUnlinkFn(vm, dirs);
+  };
+}
+
+/**
+ * Apply a linker to a vm/element pair and capture the
+ * directives created during the process.
+ *
+ * @param {Function} linker
+ * @param {Vue} vm
+ */
+
+function linkAndCapture(linker, vm) {
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV === 'production') {
+    // reset directives before every capture in production
+    // mode, so that when unlinking we don't need to splice
+    // them out (which turns out to be a perf hit).
+    // they are kept in development mode because they are
+    // useful for Vue's own tests.
+    vm._directives = [];
+  }
+  var originalDirCount = vm._directives.length;
+  linker();
+  var dirs = vm._directives.slice(originalDirCount);
+  sortDirectives(dirs);
+  for (var i = 0, l = dirs.length; i < l; i++) {
+    dirs[i]._bind();
+  }
+  return dirs;
+}
+
+/**
+ * sort directives by priority (stable sort)
+ *
+ * @param {Array} dirs
+ */
+function sortDirectives(dirs) {
+  if (dirs.length === 0) return;
+
+  var groupedMap = {};
+  var i, j, k, l;
+  var index = 0;
+  var priorities = [];
+  for (i = 0, j = dirs.length; i < j; i++) {
+    var dir = dirs[i];
+    var priority = dir.descriptor.def.priority || DEFAULT_PRIORITY;
+    var array = groupedMap[priority];
+    if (!array) {
+      array = groupedMap[priority] = [];
+      priorities.push(priority);
+    }
+    array.push(dir);
+  }
+
+  priorities.sort(function (a, b) {
+    return a > b ? -1 : a === b ? 0 : 1;
+  });
+  for (i = 0, j = priorities.length; i < j; i++) {
+    var group = groupedMap[priorities[i]];
+    for (k = 0, l = group.length; k < l; k++) {
+      dirs[index++] = group[k];
+    }
+  }
+}
+
+/**
+ * Linker functions return an unlink function that
+ * tearsdown all directives instances generated during
+ * the process.
+ *
+ * We create unlink functions with only the necessary
+ * information to avoid retaining additional closures.
+ *
+ * @param {Vue} vm
+ * @param {Array} dirs
+ * @param {Vue} [context]
+ * @param {Array} [contextDirs]
+ * @return {Function}
+ */
+
+function makeUnlinkFn(vm, dirs, context, contextDirs) {
+  function unlink(destroying) {
+    teardownDirs(vm, dirs, destroying);
+    if (context && contextDirs) {
+      teardownDirs(context, contextDirs);
+    }
+  }
+  // expose linked directives
+  unlink.dirs = dirs;
+  return unlink;
+}
+
+/**
+ * Teardown partial linked directives.
+ *
+ * @param {Vue} vm
+ * @param {Array} dirs
+ * @param {Boolean} destroying
+ */
+
+function teardownDirs(vm, dirs, destroying) {
+  var i = dirs.length;
+  while (i--) {
+    dirs[i]._teardown();
+    if (process.env.NODE_ENV !== 'production' && !destroying) {
+      vm._directives.$remove(dirs[i]);
+    }
+  }
+}
+
+/**
+ * Compile link props on an instance.
+ *
+ * @param {Vue} vm
+ * @param {Element} el
+ * @param {Object} props
+ * @param {Object} [scope]
+ * @return {Function}
+ */
+
+function compileAndLinkProps(vm, el, props, scope) {
+  var propsLinkFn = compileProps(el, props, vm);
+  var propDirs = linkAndCapture(function () {
+    propsLinkFn(vm, scope);
+  }, vm);
+  return makeUnlinkFn(vm, propDirs);
+}
+
+/**
+ * Compile the root element of an instance.
+ *
+ * 1. attrs on context container (context scope)
+ * 2. attrs on the component template root node, if
+ *    replace:true (child scope)
+ *
+ * If this is a fragment instance, we only need to compile 1.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @param {Object} contextOptions
+ * @return {Function}
+ */
+
+function compileRoot(el, options, contextOptions) {
+  var containerAttrs = options._containerAttrs;
+  var replacerAttrs = options._replacerAttrs;
+  var contextLinkFn, replacerLinkFn;
+
+  // only need to compile other attributes for
+  // non-fragment instances
+  if (el.nodeType !== 11) {
+    // for components, container and replacer need to be
+    // compiled separately and linked in different scopes.
+    if (options._asComponent) {
+      // 2. container attributes
+      if (containerAttrs && contextOptions) {
+        contextLinkFn = compileDirectives(containerAttrs, contextOptions);
+      }
+      if (replacerAttrs) {
+        // 3. replacer attributes
+        replacerLinkFn = compileDirectives(replacerAttrs, options);
+      }
+    } else {
+      // non-component, just compile as a normal element.
+      replacerLinkFn = compileDirectives(el.attributes, options);
+    }
+  } else if (process.env.NODE_ENV !== 'production' && containerAttrs) {
+    // warn container directives for fragment instances
+    var names = containerAttrs.filter(function (attr) {
+      // allow vue-loader/vueify scoped css attributes
+      return attr.name.indexOf('_v-') < 0 &&
+      // allow event listeners
+      !onRE.test(attr.name) &&
+      // allow slots
+      attr.name !== 'slot';
+    }).map(function (attr) {
+      return '"' + attr.name + '"';
+    });
+    if (names.length) {
+      var plural = names.length > 1;
+
+      var componentName = options.el.tagName.toLowerCase();
+      if (componentName === 'component' && options.name) {
+        componentName += ':' + options.name;
+      }
+
+      warn('Attribute' + (plural ? 's ' : ' ') + names.join(', ') + (plural ? ' are' : ' is') + ' ignored on component ' + '<' + componentName + '> because ' + 'the component is a fragment instance: ' + 'http://vuejs.org/guide/components.html#Fragment-Instance');
+    }
+  }
+
+  options._containerAttrs = options._replacerAttrs = null;
+  return function rootLinkFn(vm, el, scope) {
+    // link context scope dirs
+    var context = vm._context;
+    var contextDirs;
+    if (context && contextLinkFn) {
+      contextDirs = linkAndCapture(function () {
+        contextLinkFn(context, el, null, scope);
+      }, context);
+    }
+
+    // link self
+    var selfDirs = linkAndCapture(function () {
+      if (replacerLinkFn) replacerLinkFn(vm, el);
+    }, vm);
+
+    // return the unlink function that tearsdown context
+    // container directives.
+    return makeUnlinkFn(vm, selfDirs, context, contextDirs);
+  };
+}
+
+/**
+ * Compile a node and return a nodeLinkFn based on the
+ * node type.
+ *
+ * @param {Node} node
+ * @param {Object} options
+ * @return {Function|null}
+ */
+
+function compileNode(node, options) {
+  var type = node.nodeType;
+  if (type === 1 && !isScript(node)) {
+    return compileElement(node, options);
+  } else if (type === 3 && node.data.trim()) {
+    return compileTextNode(node, options);
+  } else {
+    return null;
+  }
+}
+
+/**
+ * Compile an element and return a nodeLinkFn.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Function|null}
+ */
+
+function compileElement(el, options) {
+  // preprocess textareas.
+  // textarea treats its text content as the initial value.
+  // just bind it as an attr directive for value.
+  if (el.tagName === 'TEXTAREA') {
+    // a textarea which has v-pre attr should skip complie.
+    if (getAttr(el, 'v-pre') !== null) {
+      return skip;
+    }
+    var tokens = parseText(el.value);
+    if (tokens) {
+      el.setAttribute(':value', tokensToExp(tokens));
+      el.value = '';
+    }
+  }
+  var linkFn;
+  var hasAttrs = el.hasAttributes();
+  var attrs = hasAttrs && toArray(el.attributes);
+  // check terminal directives (for & if)
+  if (hasAttrs) {
+    linkFn = checkTerminalDirectives(el, attrs, options);
+  }
+  // check element directives
+  if (!linkFn) {
+    linkFn = checkElementDirectives(el, options);
+  }
+  // check component
+  if (!linkFn) {
+    linkFn = checkComponent(el, options);
+  }
+  // normal directives
+  if (!linkFn && hasAttrs) {
+    linkFn = compileDirectives(attrs, options);
+  }
+  return linkFn;
+}
+
+/**
+ * Compile a textNode and return a nodeLinkFn.
+ *
+ * @param {TextNode} node
+ * @param {Object} options
+ * @return {Function|null} textNodeLinkFn
+ */
+
+function compileTextNode(node, options) {
+  // skip marked text nodes
+  if (node._skip) {
+    return removeText;
+  }
+
+  var tokens = parseText(node.wholeText);
+  if (!tokens) {
+    return null;
+  }
+
+  // mark adjacent text nodes as skipped,
+  // because we are using node.wholeText to compile
+  // all adjacent text nodes together. This fixes
+  // issues in IE where sometimes it splits up a single
+  // text node into multiple ones.
+  var next = node.nextSibling;
+  while (next && next.nodeType === 3) {
+    next._skip = true;
+    next = next.nextSibling;
+  }
+
+  var frag = document.createDocumentFragment();
+  var el, token;
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    token = tokens[i];
+    el = token.tag ? processTextToken(token, options) : document.createTextNode(token.value);
+    frag.appendChild(el);
+  }
+  return makeTextNodeLinkFn(tokens, frag, options);
+}
+
+/**
+ * Linker for an skipped text node.
+ *
+ * @param {Vue} vm
+ * @param {Text} node
+ */
+
+function removeText(vm, node) {
+  remove(node);
+}
+
+/**
+ * Process a single text token.
+ *
+ * @param {Object} token
+ * @param {Object} options
+ * @return {Node}
+ */
+
+function processTextToken(token, options) {
+  var el;
+  if (token.oneTime) {
+    el = document.createTextNode(token.value);
+  } else {
+    if (token.html) {
+      el = document.createComment('v-html');
+      setTokenType('html');
+    } else {
+      // IE will clean up empty textNodes during
+      // frag.cloneNode(true), so we have to give it
+      // something here...
+      el = document.createTextNode(' ');
+      setTokenType('text');
+    }
+  }
+  function setTokenType(type) {
+    if (token.descriptor) return;
+    var parsed = parseDirective(token.value);
+    token.descriptor = {
+      name: type,
+      def: directives[type],
+      expression: parsed.expression,
+      filters: parsed.filters
+    };
+  }
+  return el;
+}
+
+/**
+ * Build a function that processes a textNode.
+ *
+ * @param {Array<Object>} tokens
+ * @param {DocumentFragment} frag
+ */
+
+function makeTextNodeLinkFn(tokens, frag) {
+  return function textNodeLinkFn(vm, el, host, scope) {
+    var fragClone = frag.cloneNode(true);
+    var childNodes = toArray(fragClone.childNodes);
+    var token, value, node;
+    for (var i = 0, l = tokens.length; i < l; i++) {
+      token = tokens[i];
+      value = token.value;
+      if (token.tag) {
+        node = childNodes[i];
+        if (token.oneTime) {
+          value = (scope || vm).$eval(value);
+          if (token.html) {
+            replace(node, parseTemplate(value, true));
+          } else {
+            node.data = _toString(value);
+          }
+        } else {
+          vm._bindDir(token.descriptor, node, host, scope);
+        }
+      }
+    }
+    replace(el, fragClone);
+  };
+}
+
+/**
+ * Compile a node list and return a childLinkFn.
+ *
+ * @param {NodeList} nodeList
+ * @param {Object} options
+ * @return {Function|undefined}
+ */
+
+function compileNodeList(nodeList, options) {
+  var linkFns = [];
+  var nodeLinkFn, childLinkFn, node;
+  for (var i = 0, l = nodeList.length; i < l; i++) {
+    node = nodeList[i];
+    nodeLinkFn = compileNode(node, options);
+    childLinkFn = !(nodeLinkFn && nodeLinkFn.terminal) && node.tagName !== 'SCRIPT' && node.hasChildNodes() ? compileNodeList(node.childNodes, options) : null;
+    linkFns.push(nodeLinkFn, childLinkFn);
+  }
+  return linkFns.length ? makeChildLinkFn(linkFns) : null;
+}
+
+/**
+ * Make a child link function for a node's childNodes.
+ *
+ * @param {Array<Function>} linkFns
+ * @return {Function} childLinkFn
+ */
+
+function makeChildLinkFn(linkFns) {
+  return function childLinkFn(vm, nodes, host, scope, frag) {
+    var node, nodeLinkFn, childrenLinkFn;
+    for (var i = 0, n = 0, l = linkFns.length; i < l; n++) {
+      node = nodes[n];
+      nodeLinkFn = linkFns[i++];
+      childrenLinkFn = linkFns[i++];
+      // cache childNodes before linking parent, fix #657
+      var childNodes = toArray(node.childNodes);
+      if (nodeLinkFn) {
+        nodeLinkFn(vm, node, host, scope, frag);
+      }
+      if (childrenLinkFn) {
+        childrenLinkFn(vm, childNodes, host, scope, frag);
+      }
+    }
+  };
+}
+
+/**
+ * Check for element directives (custom elements that should
+ * be resovled as terminal directives).
+ *
+ * @param {Element} el
+ * @param {Object} options
+ */
+
+function checkElementDirectives(el, options) {
+  var tag = el.tagName.toLowerCase();
+  if (commonTagRE.test(tag)) {
+    return;
+  }
+  var def = resolveAsset(options, 'elementDirectives', tag);
+  if (def) {
+    return makeTerminalNodeLinkFn(el, tag, '', options, def);
+  }
+}
+
+/**
+ * Check if an element is a component. If yes, return
+ * a component link function.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Function|undefined}
+ */
+
+function checkComponent(el, options) {
+  var component = checkComponentAttr(el, options);
+  if (component) {
+    var ref = findRef(el);
+    var descriptor = {
+      name: 'component',
+      ref: ref,
+      expression: component.id,
+      def: internalDirectives.component,
+      modifiers: {
+        literal: !component.dynamic
+      }
+    };
+    var componentLinkFn = function componentLinkFn(vm, el, host, scope, frag) {
+      if (ref) {
+        defineReactive((scope || vm).$refs, ref, null);
+      }
+      vm._bindDir(descriptor, el, host, scope, frag);
+    };
+    componentLinkFn.terminal = true;
+    return componentLinkFn;
+  }
+}
+
+/**
+ * Check an element for terminal directives in fixed order.
+ * If it finds one, return a terminal link function.
+ *
+ * @param {Element} el
+ * @param {Array} attrs
+ * @param {Object} options
+ * @return {Function} terminalLinkFn
+ */
+
+function checkTerminalDirectives(el, attrs, options) {
+  // skip v-pre
+  if (getAttr(el, 'v-pre') !== null) {
+    return skip;
+  }
+  // skip v-else block, but only if following v-if
+  if (el.hasAttribute('v-else')) {
+    var prev = el.previousElementSibling;
+    if (prev && prev.hasAttribute('v-if')) {
+      return skip;
+    }
+  }
+
+  var attr, name, value, modifiers, matched, dirName, rawName, arg, def, termDef;
+  for (var i = 0, j = attrs.length; i < j; i++) {
+    attr = attrs[i];
+    name = attr.name.replace(modifierRE, '');
+    if (matched = name.match(dirAttrRE)) {
+      def = resolveAsset(options, 'directives', matched[1]);
+      if (def && def.terminal) {
+        if (!termDef || (def.priority || DEFAULT_TERMINAL_PRIORITY) > termDef.priority) {
+          termDef = def;
+          rawName = attr.name;
+          modifiers = parseModifiers(attr.name);
+          value = attr.value;
+          dirName = matched[1];
+          arg = matched[2];
+        }
+      }
+    }
+  }
+
+  if (termDef) {
+    return makeTerminalNodeLinkFn(el, dirName, value, options, termDef, rawName, arg, modifiers);
+  }
+}
+
+function skip() {}
+skip.terminal = true;
+
+/**
+ * Build a node link function for a terminal directive.
+ * A terminal link function terminates the current
+ * compilation recursion and handles compilation of the
+ * subtree in the directive.
+ *
+ * @param {Element} el
+ * @param {String} dirName
+ * @param {String} value
+ * @param {Object} options
+ * @param {Object} def
+ * @param {String} [rawName]
+ * @param {String} [arg]
+ * @param {Object} [modifiers]
+ * @return {Function} terminalLinkFn
+ */
+
+function makeTerminalNodeLinkFn(el, dirName, value, options, def, rawName, arg, modifiers) {
+  var parsed = parseDirective(value);
+  var descriptor = {
+    name: dirName,
+    arg: arg,
+    expression: parsed.expression,
+    filters: parsed.filters,
+    raw: value,
+    attr: rawName,
+    modifiers: modifiers,
+    def: def
+  };
+  // check ref for v-for, v-if and router-view
+  if (dirName === 'for' || dirName === 'router-view') {
+    descriptor.ref = findRef(el);
+  }
+  var fn = function terminalNodeLinkFn(vm, el, host, scope, frag) {
+    if (descriptor.ref) {
+      defineReactive((scope || vm).$refs, descriptor.ref, null);
+    }
+    vm._bindDir(descriptor, el, host, scope, frag);
+  };
+  fn.terminal = true;
+  return fn;
+}
+
+/**
+ * Compile the directives on an element and return a linker.
+ *
+ * @param {Array|NamedNodeMap} attrs
+ * @param {Object} options
+ * @return {Function}
+ */
+
+function compileDirectives(attrs, options) {
+  var i = attrs.length;
+  var dirs = [];
+  var attr, name, value, rawName, rawValue, dirName, arg, modifiers, dirDef, tokens, matched;
+  while (i--) {
+    attr = attrs[i];
+    name = rawName = attr.name;
+    value = rawValue = attr.value;
+    tokens = parseText(value);
+    // reset arg
+    arg = null;
+    // check modifiers
+    modifiers = parseModifiers(name);
+    name = name.replace(modifierRE, '');
+
+    // attribute interpolations
+    if (tokens) {
+      value = tokensToExp(tokens);
+      arg = name;
+      pushDir('bind', directives.bind, tokens);
+      // warn against mixing mustaches with v-bind
+      if (process.env.NODE_ENV !== 'production') {
+        if (name === 'class' && Array.prototype.some.call(attrs, function (attr) {
+          return attr.name === ':class' || attr.name === 'v-bind:class';
+        })) {
+          warn('class="' + rawValue + '": Do not mix mustache interpolation ' + 'and v-bind for "class" on the same element. Use one or the other.', options);
+        }
+      }
+    } else
+
+      // special attribute: transition
+      if (transitionRE.test(name)) {
+        modifiers.literal = !bindRE.test(name);
+        pushDir('transition', internalDirectives.transition);
+      } else
+
+        // event handlers
+        if (onRE.test(name)) {
+          arg = name.replace(onRE, '');
+          pushDir('on', directives.on);
+        } else
+
+          // attribute bindings
+          if (bindRE.test(name)) {
+            dirName = name.replace(bindRE, '');
+            if (dirName === 'style' || dirName === 'class') {
+              pushDir(dirName, internalDirectives[dirName]);
+            } else {
+              arg = dirName;
+              pushDir('bind', directives.bind);
+            }
+          } else
+
+            // normal directives
+            if (matched = name.match(dirAttrRE)) {
+              dirName = matched[1];
+              arg = matched[2];
+
+              // skip v-else (when used with v-show)
+              if (dirName === 'else') {
+                continue;
+              }
+
+              dirDef = resolveAsset(options, 'directives', dirName, true);
+              if (dirDef) {
+                pushDir(dirName, dirDef);
+              }
+            }
+  }
+
+  /**
+   * Push a directive.
+   *
+   * @param {String} dirName
+   * @param {Object|Function} def
+   * @param {Array} [interpTokens]
+   */
+
+  function pushDir(dirName, def, interpTokens) {
+    var hasOneTimeToken = interpTokens && hasOneTime(interpTokens);
+    var parsed = !hasOneTimeToken && parseDirective(value);
+    dirs.push({
+      name: dirName,
+      attr: rawName,
+      raw: rawValue,
+      def: def,
+      arg: arg,
+      modifiers: modifiers,
+      // conversion from interpolation strings with one-time token
+      // to expression is differed until directive bind time so that we
+      // have access to the actual vm context for one-time bindings.
+      expression: parsed && parsed.expression,
+      filters: parsed && parsed.filters,
+      interp: interpTokens,
+      hasOneTime: hasOneTimeToken
+    });
+  }
+
+  if (dirs.length) {
+    return makeNodeLinkFn(dirs);
+  }
+}
+
+/**
+ * Parse modifiers from directive attribute name.
+ *
+ * @param {String} name
+ * @return {Object}
+ */
+
+function parseModifiers(name) {
+  var res = Object.create(null);
+  var match = name.match(modifierRE);
+  if (match) {
+    var i = match.length;
+    while (i--) {
+      res[match[i].slice(1)] = true;
+    }
+  }
+  return res;
+}
+
+/**
+ * Build a link function for all directives on a single node.
+ *
+ * @param {Array} directives
+ * @return {Function} directivesLinkFn
+ */
+
+function makeNodeLinkFn(directives) {
+  return function nodeLinkFn(vm, el, host, scope, frag) {
+    // reverse apply because it's sorted low to high
+    var i = directives.length;
+    while (i--) {
+      vm._bindDir(directives[i], el, host, scope, frag);
+    }
+  };
+}
+
+/**
+ * Check if an interpolation string contains one-time tokens.
+ *
+ * @param {Array} tokens
+ * @return {Boolean}
+ */
+
+function hasOneTime(tokens) {
+  var i = tokens.length;
+  while (i--) {
+    if (tokens[i].oneTime) return true;
+  }
+}
+
+function isScript(el) {
+  return el.tagName === 'SCRIPT' && (!el.hasAttribute('type') || el.getAttribute('type') === 'text/javascript');
+}
+
+var specialCharRE = /[^\w\-:\.]/;
+
+/**
+ * Process an element or a DocumentFragment based on a
+ * instance option object. This allows us to transclude
+ * a template node/fragment before the instance is created,
+ * so the processed fragment can then be cloned and reused
+ * in v-for.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Element|DocumentFragment}
+ */
+
+function transclude(el, options) {
+  // extract container attributes to pass them down
+  // to compiler, because they need to be compiled in
+  // parent scope. we are mutating the options object here
+  // assuming the same object will be used for compile
+  // right after this.
+  if (options) {
+    options._containerAttrs = extractAttrs(el);
+  }
+  // for template tags, what we want is its content as
+  // a documentFragment (for fragment instances)
+  if (isTemplate(el)) {
+    el = parseTemplate(el);
+  }
+  if (options) {
+    if (options._asComponent && !options.template) {
+      options.template = '<slot></slot>';
+    }
+    if (options.template) {
+      options._content = extractContent(el);
+      el = transcludeTemplate(el, options);
+    }
+  }
+  if (isFragment(el)) {
+    // anchors for fragment instance
+    // passing in `persist: true` to avoid them being
+    // discarded by IE during template cloning
+    prepend(createAnchor('v-start', true), el);
+    el.appendChild(createAnchor('v-end', true));
+  }
+  return el;
+}
+
+/**
+ * Process the template option.
+ * If the replace option is true this will swap the $el.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Element|DocumentFragment}
+ */
+
+function transcludeTemplate(el, options) {
+  var template = options.template;
+  var frag = parseTemplate(template, true);
+  if (frag) {
+    var replacer = frag.firstChild;
+    if (!replacer) {
+      return frag;
+    }
+    var tag = replacer.tagName && replacer.tagName.toLowerCase();
+    if (options.replace) {
+      /* istanbul ignore if */
+      if (el === document.body) {
+        process.env.NODE_ENV !== 'production' && warn('You are mounting an instance with a template to ' + '<body>. This will replace <body> entirely. You ' + 'should probably use `replace: false` here.');
+      }
+      // there are many cases where the instance must
+      // become a fragment instance: basically anything that
+      // can create more than 1 root nodes.
+      if (
+      // multi-children template
+      frag.childNodes.length > 1 ||
+      // non-element template
+      replacer.nodeType !== 1 ||
+      // single nested component
+      tag === 'component' || resolveAsset(options, 'components', tag) || hasBindAttr(replacer, 'is') ||
+      // element directive
+      resolveAsset(options, 'elementDirectives', tag) ||
+      // for block
+      replacer.hasAttribute('v-for') ||
+      // if block
+      replacer.hasAttribute('v-if')) {
+        return frag;
+      } else {
+        options._replacerAttrs = extractAttrs(replacer);
+        mergeAttrs(el, replacer);
+        return replacer;
+      }
+    } else {
+      el.appendChild(frag);
+      return el;
+    }
+  } else {
+    process.env.NODE_ENV !== 'production' && warn('Invalid template option: ' + template);
+  }
+}
+
+/**
+ * Helper to extract a component container's attributes
+ * into a plain object array.
+ *
+ * @param {Element} el
+ * @return {Array}
+ */
+
+function extractAttrs(el) {
+  if (el.nodeType === 1 && el.hasAttributes()) {
+    return toArray(el.attributes);
+  }
+}
+
+/**
+ * Merge the attributes of two elements, and make sure
+ * the class names are merged properly.
+ *
+ * @param {Element} from
+ * @param {Element} to
+ */
+
+function mergeAttrs(from, to) {
+  var attrs = from.attributes;
+  var i = attrs.length;
+  var name, value;
+  while (i--) {
+    name = attrs[i].name;
+    value = attrs[i].value;
+    if (!to.hasAttribute(name) && !specialCharRE.test(name)) {
+      to.setAttribute(name, value);
+    } else if (name === 'class' && !parseText(value) && (value = value.trim())) {
+      value.split(/\s+/).forEach(function (cls) {
+        addClass(to, cls);
+      });
+    }
+  }
+}
+
+/**
+ * Scan and determine slot content distribution.
+ * We do this during transclusion instead at compile time so that
+ * the distribution is decoupled from the compilation order of
+ * the slots.
+ *
+ * @param {Element|DocumentFragment} template
+ * @param {Element} content
+ * @param {Vue} vm
+ */
+
+function resolveSlots(vm, content) {
+  if (!content) {
+    return;
+  }
+  var contents = vm._slotContents = Object.create(null);
+  var el, name;
+  for (var i = 0, l = content.children.length; i < l; i++) {
+    el = content.children[i];
+    /* eslint-disable no-cond-assign */
+    if (name = el.getAttribute('slot')) {
+      (contents[name] || (contents[name] = [])).push(el);
+    }
+    /* eslint-enable no-cond-assign */
+    if (process.env.NODE_ENV !== 'production' && getBindAttr(el, 'slot')) {
+      warn('The "slot" attribute must be static.', vm.$parent);
+    }
+  }
+  for (name in contents) {
+    contents[name] = extractFragment(contents[name], content);
+  }
+  if (content.hasChildNodes()) {
+    var nodes = content.childNodes;
+    if (nodes.length === 1 && nodes[0].nodeType === 3 && !nodes[0].data.trim()) {
+      return;
+    }
+    contents['default'] = extractFragment(content.childNodes, content);
+  }
+}
+
+/**
+ * Extract qualified content nodes from a node list.
+ *
+ * @param {NodeList} nodes
+ * @return {DocumentFragment}
+ */
+
+function extractFragment(nodes, parent) {
+  var frag = document.createDocumentFragment();
+  nodes = toArray(nodes);
+  for (var i = 0, l = nodes.length; i < l; i++) {
+    var node = nodes[i];
+    if (isTemplate(node) && !node.hasAttribute('v-if') && !node.hasAttribute('v-for')) {
+      parent.removeChild(node);
+      node = parseTemplate(node, true);
+    }
+    frag.appendChild(node);
+  }
+  return frag;
+}
+
+
+
+var compiler = Object.freeze({
+	compile: compile,
+	compileAndLinkProps: compileAndLinkProps,
+	compileRoot: compileRoot,
+	transclude: transclude,
+	resolveSlots: resolveSlots
+});
+
+function stateMixin (Vue) {
+  /**
+   * Accessor for `$data` property, since setting $data
+   * requires observing the new object and updating
+   * proxied properties.
+   */
+
+  Object.defineProperty(Vue.prototype, '$data', {
+    get: function get() {
+      return this._data;
+    },
+    set: function set(newData) {
+      if (newData !== this._data) {
+        this._setData(newData);
+      }
+    }
+  });
+
+  /**
+   * Setup the scope of an instance, which contains:
+   * - observed data
+   * - computed properties
+   * - user methods
+   * - meta properties
+   */
+
+  Vue.prototype._initState = function () {
+    this._initProps();
+    this._initMeta();
+    this._initMethods();
+    this._initData();
+    this._initComputed();
+  };
+
+  /**
+   * Initialize props.
+   */
+
+  Vue.prototype._initProps = function () {
+    var options = this.$options;
+    var el = options.el;
+    var props = options.props;
+    if (props && !el) {
+      process.env.NODE_ENV !== 'production' && warn('Props will not be compiled if no `el` option is ' + 'provided at instantiation.', this);
+    }
+    // make sure to convert string selectors into element now
+    el = options.el = query(el);
+    this._propsUnlinkFn = el && el.nodeType === 1 && props
+    // props must be linked in proper scope if inside v-for
+    ? compileAndLinkProps(this, el, props, this._scope) : null;
+  };
+
+  /**
+   * Initialize the data.
+   */
+
+  Vue.prototype._initData = function () {
+    var dataFn = this.$options.data;
+    var data = this._data = dataFn ? dataFn() : {};
+    if (!isPlainObject(data)) {
+      data = {};
+      process.env.NODE_ENV !== 'production' && warn('data functions should return an object.', this);
+    }
+    var props = this._props;
+    // proxy data on instance
+    var keys = Object.keys(data);
+    var i, key;
+    i = keys.length;
+    while (i--) {
+      key = keys[i];
+      // there are two scenarios where we can proxy a data key:
+      // 1. it's not already defined as a prop
+      // 2. it's provided via a instantiation option AND there are no
+      //    template prop present
+      if (!props || !hasOwn(props, key)) {
+        this._proxy(key);
+      } else if (process.env.NODE_ENV !== 'production') {
+        warn('Data field "' + key + '" is already defined ' + 'as a prop. To provide default value for a prop, use the "default" ' + 'prop option; if you want to pass prop values to an instantiation ' + 'call, use the "propsData" option.', this);
+      }
+    }
+    // observe data
+    observe(data, this);
+  };
+
+  /**
+   * Swap the instance's $data. Called in $data's setter.
+   *
+   * @param {Object} newData
+   */
+
+  Vue.prototype._setData = function (newData) {
+    newData = newData || {};
+    var oldData = this._data;
+    this._data = newData;
+    var keys, key, i;
+    // unproxy keys not present in new data
+    keys = Object.keys(oldData);
+    i = keys.length;
+    while (i--) {
+      key = keys[i];
+      if (!(key in newData)) {
+        this._unproxy(key);
+      }
+    }
+    // proxy keys not already proxied,
+    // and trigger change for changed values
+    keys = Object.keys(newData);
+    i = keys.length;
+    while (i--) {
+      key = keys[i];
+      if (!hasOwn(this, key)) {
+        // new property
+        this._proxy(key);
+      }
+    }
+    oldData.__ob__.removeVm(this);
+    observe(newData, this);
+    this._digest();
+  };
+
+  /**
+   * Proxy a property, so that
+   * vm.prop === vm._data.prop
+   *
+   * @param {String} key
+   */
+
+  Vue.prototype._proxy = function (key) {
+    if (!isReserved(key)) {
+      // need to store ref to self here
+      // because these getter/setters might
+      // be called by child scopes via
+      // prototype inheritance.
+      var self = this;
+      Object.defineProperty(self, key, {
+        configurable: true,
+        enumerable: true,
+        get: function proxyGetter() {
+          return self._data[key];
+        },
+        set: function proxySetter(val) {
+          self._data[key] = val;
+        }
+      });
+    }
+  };
+
+  /**
+   * Unproxy a property.
+   *
+   * @param {String} key
+   */
+
+  Vue.prototype._unproxy = function (key) {
+    if (!isReserved(key)) {
+      delete this[key];
+    }
+  };
+
+  /**
+   * Force update on every watcher in scope.
+   */
+
+  Vue.prototype._digest = function () {
+    for (var i = 0, l = this._watchers.length; i < l; i++) {
+      this._watchers[i].update(true); // shallow updates
+    }
+  };
+
+  /**
+   * Setup computed properties. They are essentially
+   * special getter/setters
+   */
+
+  function noop() {}
+  Vue.prototype._initComputed = function () {
+    var computed = this.$options.computed;
+    if (computed) {
+      for (var key in computed) {
+        var userDef = computed[key];
+        var def = {
+          enumerable: true,
+          configurable: true
+        };
+        if (typeof userDef === 'function') {
+          def.get = makeComputedGetter(userDef, this);
+          def.set = noop;
+        } else {
+          def.get = userDef.get ? userDef.cache !== false ? makeComputedGetter(userDef.get, this) : bind(userDef.get, this) : noop;
+          def.set = userDef.set ? bind(userDef.set, this) : noop;
+        }
+        Object.defineProperty(this, key, def);
+      }
+    }
+  };
+
+  function makeComputedGetter(getter, owner) {
+    var watcher = new Watcher(owner, getter, null, {
+      lazy: true
+    });
+    return function computedGetter() {
+      if (watcher.dirty) {
+        watcher.evaluate();
+      }
+      if (Dep.target) {
+        watcher.depend();
+      }
+      return watcher.value;
+    };
+  }
+
+  /**
+   * Setup instance methods. Methods must be bound to the
+   * instance since they might be passed down as a prop to
+   * child components.
+   */
+
+  Vue.prototype._initMethods = function () {
+    var methods = this.$options.methods;
+    if (methods) {
+      for (var key in methods) {
+        this[key] = bind(methods[key], this);
+      }
+    }
+  };
+
+  /**
+   * Initialize meta information like $index, $key & $value.
+   */
+
+  Vue.prototype._initMeta = function () {
+    var metas = this.$options._meta;
+    if (metas) {
+      for (var key in metas) {
+        defineReactive(this, key, metas[key]);
+      }
+    }
+  };
+}
+
+var eventRE = /^v-on:|^@/;
+
+function eventsMixin (Vue) {
+  /**
+   * Setup the instance's option events & watchers.
+   * If the value is a string, we pull it from the
+   * instance's methods by name.
+   */
+
+  Vue.prototype._initEvents = function () {
+    var options = this.$options;
+    if (options._asComponent) {
+      registerComponentEvents(this, options.el);
+    }
+    registerCallbacks(this, '$on', options.events);
+    registerCallbacks(this, '$watch', options.watch);
+  };
+
+  /**
+   * Register v-on events on a child component
+   *
+   * @param {Vue} vm
+   * @param {Element} el
+   */
+
+  function registerComponentEvents(vm, el) {
+    var attrs = el.attributes;
+    var name, value, handler;
+    for (var i = 0, l = attrs.length; i < l; i++) {
+      name = attrs[i].name;
+      if (eventRE.test(name)) {
+        name = name.replace(eventRE, '');
+        // force the expression into a statement so that
+        // it always dynamically resolves the method to call (#2670)
+        // kinda ugly hack, but does the job.
+        value = attrs[i].value;
+        if (isSimplePath(value)) {
+          value += '.apply(this, $arguments)';
+        }
+        handler = (vm._scope || vm._context).$eval(value, true);
+        handler._fromParent = true;
+        vm.$on(name.replace(eventRE), handler);
+      }
+    }
+  }
+
+  /**
+   * Register callbacks for option events and watchers.
+   *
+   * @param {Vue} vm
+   * @param {String} action
+   * @param {Object} hash
+   */
+
+  function registerCallbacks(vm, action, hash) {
+    if (!hash) return;
+    var handlers, key, i, j;
+    for (key in hash) {
+      handlers = hash[key];
+      if (isArray(handlers)) {
+        for (i = 0, j = handlers.length; i < j; i++) {
+          register(vm, action, key, handlers[i]);
+        }
+      } else {
+        register(vm, action, key, handlers);
+      }
+    }
+  }
+
+  /**
+   * Helper to register an event/watch callback.
+   *
+   * @param {Vue} vm
+   * @param {String} action
+   * @param {String} key
+   * @param {Function|String|Object} handler
+   * @param {Object} [options]
+   */
+
+  function register(vm, action, key, handler, options) {
+    var type = typeof handler;
+    if (type === 'function') {
+      vm[action](key, handler, options);
+    } else if (type === 'string') {
+      var methods = vm.$options.methods;
+      var method = methods && methods[handler];
+      if (method) {
+        vm[action](key, method, options);
+      } else {
+        process.env.NODE_ENV !== 'production' && warn('Unknown method: "' + handler + '" when ' + 'registering callback for ' + action + ': "' + key + '".', vm);
+      }
+    } else if (handler && type === 'object') {
+      register(vm, action, key, handler.handler, handler);
+    }
+  }
+
+  /**
+   * Setup recursive attached/detached calls
+   */
+
+  Vue.prototype._initDOMHooks = function () {
+    this.$on('hook:attached', onAttached);
+    this.$on('hook:detached', onDetached);
+  };
+
+  /**
+   * Callback to recursively call attached hook on children
+   */
+
+  function onAttached() {
+    if (!this._isAttached) {
+      this._isAttached = true;
+      this.$children.forEach(callAttach);
+    }
+  }
+
+  /**
+   * Iterator to call attached hook
+   *
+   * @param {Vue} child
+   */
+
+  function callAttach(child) {
+    if (!child._isAttached && inDoc(child.$el)) {
+      child._callHook('attached');
+    }
+  }
+
+  /**
+   * Callback to recursively call detached hook on children
+   */
+
+  function onDetached() {
+    if (this._isAttached) {
+      this._isAttached = false;
+      this.$children.forEach(callDetach);
+    }
+  }
+
+  /**
+   * Iterator to call detached hook
+   *
+   * @param {Vue} child
+   */
+
+  function callDetach(child) {
+    if (child._isAttached && !inDoc(child.$el)) {
+      child._callHook('detached');
+    }
+  }
+
+  /**
+   * Trigger all handlers for a hook
+   *
+   * @param {String} hook
+   */
+
+  Vue.prototype._callHook = function (hook) {
+    this.$emit('pre-hook:' + hook);
+    var handlers = this.$options[hook];
+    if (handlers) {
+      for (var i = 0, j = handlers.length; i < j; i++) {
+        handlers[i].call(this);
+      }
+    }
+    this.$emit('hook:' + hook);
+  };
+}
+
+function noop$1() {}
+
+/**
+ * A directive links a DOM element with a piece of data,
+ * which is the result of evaluating an expression.
+ * It registers a watcher with the expression and calls
+ * the DOM update function when a change is triggered.
+ *
+ * @param {Object} descriptor
+ *                 - {String} name
+ *                 - {Object} def
+ *                 - {String} expression
+ *                 - {Array<Object>} [filters]
+ *                 - {Object} [modifiers]
+ *                 - {Boolean} literal
+ *                 - {String} attr
+ *                 - {String} arg
+ *                 - {String} raw
+ *                 - {String} [ref]
+ *                 - {Array<Object>} [interp]
+ *                 - {Boolean} [hasOneTime]
+ * @param {Vue} vm
+ * @param {Node} el
+ * @param {Vue} [host] - transclusion host component
+ * @param {Object} [scope] - v-for scope
+ * @param {Fragment} [frag] - owner fragment
+ * @constructor
+ */
+function Directive(descriptor, vm, el, host, scope, frag) {
+  this.vm = vm;
+  this.el = el;
+  // copy descriptor properties
+  this.descriptor = descriptor;
+  this.name = descriptor.name;
+  this.expression = descriptor.expression;
+  this.arg = descriptor.arg;
+  this.modifiers = descriptor.modifiers;
+  this.filters = descriptor.filters;
+  this.literal = this.modifiers && this.modifiers.literal;
+  // private
+  this._locked = false;
+  this._bound = false;
+  this._listeners = null;
+  // link context
+  this._host = host;
+  this._scope = scope;
+  this._frag = frag;
+  // store directives on node in dev mode
+  if (process.env.NODE_ENV !== 'production' && this.el) {
+    this.el._vue_directives = this.el._vue_directives || [];
+    this.el._vue_directives.push(this);
+  }
+}
+
+/**
+ * Initialize the directive, mixin definition properties,
+ * setup the watcher, call definition bind() and update()
+ * if present.
+ */
+
+Directive.prototype._bind = function () {
+  var name = this.name;
+  var descriptor = this.descriptor;
+
+  // remove attribute
+  if ((name !== 'cloak' || this.vm._isCompiled) && this.el && this.el.removeAttribute) {
+    var attr = descriptor.attr || 'v-' + name;
+    this.el.removeAttribute(attr);
+  }
+
+  // copy def properties
+  var def = descriptor.def;
+  if (typeof def === 'function') {
+    this.update = def;
+  } else {
+    extend(this, def);
+  }
+
+  // setup directive params
+  this._setupParams();
+
+  // initial bind
+  if (this.bind) {
+    this.bind();
+  }
+  this._bound = true;
+
+  if (this.literal) {
+    this.update && this.update(descriptor.raw);
+  } else if ((this.expression || this.modifiers) && (this.update || this.twoWay) && !this._checkStatement()) {
+    // wrapped updater for context
+    var dir = this;
+    if (this.update) {
+      this._update = function (val, oldVal) {
+        if (!dir._locked) {
+          dir.update(val, oldVal);
+        }
+      };
+    } else {
+      this._update = noop$1;
+    }
+    var preProcess = this._preProcess ? bind(this._preProcess, this) : null;
+    var postProcess = this._postProcess ? bind(this._postProcess, this) : null;
+    var watcher = this._watcher = new Watcher(this.vm, this.expression, this._update, // callback
+    {
+      filters: this.filters,
+      twoWay: this.twoWay,
+      deep: this.deep,
+      preProcess: preProcess,
+      postProcess: postProcess,
+      scope: this._scope
+    });
+    // v-model with inital inline value need to sync back to
+    // model instead of update to DOM on init. They would
+    // set the afterBind hook to indicate that.
+    if (this.afterBind) {
+      this.afterBind();
+    } else if (this.update) {
+      this.update(watcher.value);
+    }
+  }
+};
+
+/**
+ * Setup all param attributes, e.g. track-by,
+ * transition-mode, etc...
+ */
+
+Directive.prototype._setupParams = function () {
+  if (!this.params) {
+    return;
+  }
+  var params = this.params;
+  // swap the params array with a fresh object.
+  this.params = Object.create(null);
+  var i = params.length;
+  var key, val, mappedKey;
+  while (i--) {
+    key = hyphenate(params[i]);
+    mappedKey = camelize(key);
+    val = getBindAttr(this.el, key);
+    if (val != null) {
+      // dynamic
+      this._setupParamWatcher(mappedKey, val);
+    } else {
+      // static
+      val = getAttr(this.el, key);
+      if (val != null) {
+        this.params[mappedKey] = val === '' ? true : val;
+      }
+    }
+  }
+};
+
+/**
+ * Setup a watcher for a dynamic param.
+ *
+ * @param {String} key
+ * @param {String} expression
+ */
+
+Directive.prototype._setupParamWatcher = function (key, expression) {
+  var self = this;
+  var called = false;
+  var unwatch = (this._scope || this.vm).$watch(expression, function (val, oldVal) {
+    self.params[key] = val;
+    // since we are in immediate mode,
+    // only call the param change callbacks if this is not the first update.
+    if (called) {
+      var cb = self.paramWatchers && self.paramWatchers[key];
+      if (cb) {
+        cb.call(self, val, oldVal);
+      }
+    } else {
+      called = true;
+    }
+  }, {
+    immediate: true,
+    user: false
+  });(this._paramUnwatchFns || (this._paramUnwatchFns = [])).push(unwatch);
+};
+
+/**
+ * Check if the directive is a function caller
+ * and if the expression is a callable one. If both true,
+ * we wrap up the expression and use it as the event
+ * handler.
+ *
+ * e.g. on-click="a++"
+ *
+ * @return {Boolean}
+ */
+
+Directive.prototype._checkStatement = function () {
+  var expression = this.expression;
+  if (expression && this.acceptStatement && !isSimplePath(expression)) {
+    var fn = parseExpression$1(expression).get;
+    var scope = this._scope || this.vm;
+    var handler = function handler(e) {
+      scope.$event = e;
+      fn.call(scope, scope);
+      scope.$event = null;
+    };
+    if (this.filters) {
+      handler = scope._applyFilters(handler, null, this.filters);
+    }
+    this.update(handler);
+    return true;
+  }
+};
+
+/**
+ * Set the corresponding value with the setter.
+ * This should only be used in two-way directives
+ * e.g. v-model.
+ *
+ * @param {*} value
+ * @public
+ */
+
+Directive.prototype.set = function (value) {
+  /* istanbul ignore else */
+  if (this.twoWay) {
+    this._withLock(function () {
+      this._watcher.set(value);
+    });
+  } else if (process.env.NODE_ENV !== 'production') {
+    warn('Directive.set() can only be used inside twoWay' + 'directives.');
+  }
+};
+
+/**
+ * Execute a function while preventing that function from
+ * triggering updates on this directive instance.
+ *
+ * @param {Function} fn
+ */
+
+Directive.prototype._withLock = function (fn) {
+  var self = this;
+  self._locked = true;
+  fn.call(self);
+  nextTick(function () {
+    self._locked = false;
+  });
+};
+
+/**
+ * Convenience method that attaches a DOM event listener
+ * to the directive element and autometically tears it down
+ * during unbind.
+ *
+ * @param {String} event
+ * @param {Function} handler
+ * @param {Boolean} [useCapture]
+ */
+
+Directive.prototype.on = function (event, handler, useCapture) {
+  on(this.el, event, handler, useCapture);(this._listeners || (this._listeners = [])).push([event, handler]);
+};
+
+/**
+ * Teardown the watcher and call unbind.
+ */
+
+Directive.prototype._teardown = function () {
+  if (this._bound) {
+    this._bound = false;
+    if (this.unbind) {
+      this.unbind();
+    }
+    if (this._watcher) {
+      this._watcher.teardown();
+    }
+    var listeners = this._listeners;
+    var i;
+    if (listeners) {
+      i = listeners.length;
+      while (i--) {
+        off(this.el, listeners[i][0], listeners[i][1]);
+      }
+    }
+    var unwatchFns = this._paramUnwatchFns;
+    if (unwatchFns) {
+      i = unwatchFns.length;
+      while (i--) {
+        unwatchFns[i]();
+      }
+    }
+    if (process.env.NODE_ENV !== 'production' && this.el) {
+      this.el._vue_directives.$remove(this);
+    }
+    this.vm = this.el = this._watcher = this._listeners = null;
+  }
+};
+
+function lifecycleMixin (Vue) {
+  /**
+   * Update v-ref for component.
+   *
+   * @param {Boolean} remove
+   */
+
+  Vue.prototype._updateRef = function (remove) {
+    var ref = this.$options._ref;
+    if (ref) {
+      var refs = (this._scope || this._context).$refs;
+      if (remove) {
+        if (refs[ref] === this) {
+          refs[ref] = null;
+        }
+      } else {
+        refs[ref] = this;
+      }
+    }
+  };
+
+  /**
+   * Transclude, compile and link element.
+   *
+   * If a pre-compiled linker is available, that means the
+   * passed in element will be pre-transcluded and compiled
+   * as well - all we need to do is to call the linker.
+   *
+   * Otherwise we need to call transclude/compile/link here.
+   *
+   * @param {Element} el
+   */
+
+  Vue.prototype._compile = function (el) {
+    var options = this.$options;
+
+    // transclude and init element
+    // transclude can potentially replace original
+    // so we need to keep reference; this step also injects
+    // the template and caches the original attributes
+    // on the container node and replacer node.
+    var original = el;
+    el = transclude(el, options);
+    this._initElement(el);
+
+    // handle v-pre on root node (#2026)
+    if (el.nodeType === 1 && getAttr(el, 'v-pre') !== null) {
+      return;
+    }
+
+    // root is always compiled per-instance, because
+    // container attrs and props can be different every time.
+    var contextOptions = this._context && this._context.$options;
+    var rootLinker = compileRoot(el, options, contextOptions);
+
+    // resolve slot distribution
+    resolveSlots(this, options._content);
+
+    // compile and link the rest
+    var contentLinkFn;
+    var ctor = this.constructor;
+    // component compilation can be cached
+    // as long as it's not using inline-template
+    if (options._linkerCachable) {
+      contentLinkFn = ctor.linker;
+      if (!contentLinkFn) {
+        contentLinkFn = ctor.linker = compile(el, options);
+      }
+    }
+
+    // link phase
+    // make sure to link root with prop scope!
+    var rootUnlinkFn = rootLinker(this, el, this._scope);
+    var contentUnlinkFn = contentLinkFn ? contentLinkFn(this, el) : compile(el, options)(this, el);
+
+    // register composite unlink function
+    // to be called during instance destruction
+    this._unlinkFn = function () {
+      rootUnlinkFn();
+      // passing destroying: true to avoid searching and
+      // splicing the directives
+      contentUnlinkFn(true);
+    };
+
+    // finally replace original
+    if (options.replace) {
+      replace(original, el);
+    }
+
+    this._isCompiled = true;
+    this._callHook('compiled');
+  };
+
+  /**
+   * Initialize instance element. Called in the public
+   * $mount() method.
+   *
+   * @param {Element} el
+   */
+
+  Vue.prototype._initElement = function (el) {
+    if (isFragment(el)) {
+      this._isFragment = true;
+      this.$el = this._fragmentStart = el.firstChild;
+      this._fragmentEnd = el.lastChild;
+      // set persisted text anchors to empty
+      if (this._fragmentStart.nodeType === 3) {
+        this._fragmentStart.data = this._fragmentEnd.data = '';
+      }
+      this._fragment = el;
+    } else {
+      this.$el = el;
+    }
+    this.$el.__vue__ = this;
+    this._callHook('beforeCompile');
+  };
+
+  /**
+   * Create and bind a directive to an element.
+   *
+   * @param {Object} descriptor - parsed directive descriptor
+   * @param {Node} node   - target node
+   * @param {Vue} [host] - transclusion host component
+   * @param {Object} [scope] - v-for scope
+   * @param {Fragment} [frag] - owner fragment
+   */
+
+  Vue.prototype._bindDir = function (descriptor, node, host, scope, frag) {
+    this._directives.push(new Directive(descriptor, this, node, host, scope, frag));
+  };
+
+  /**
+   * Teardown an instance, unobserves the data, unbind all the
+   * directives, turn off all the event listeners, etc.
+   *
+   * @param {Boolean} remove - whether to remove the DOM node.
+   * @param {Boolean} deferCleanup - if true, defer cleanup to
+   *                                 be called later
+   */
+
+  Vue.prototype._destroy = function (remove, deferCleanup) {
+    if (this._isBeingDestroyed) {
+      if (!deferCleanup) {
+        this._cleanup();
+      }
+      return;
+    }
+
+    var destroyReady;
+    var pendingRemoval;
+
+    var self = this;
+    // Cleanup should be called either synchronously or asynchronoysly as
+    // callback of this.$remove(), or if remove and deferCleanup are false.
+    // In any case it should be called after all other removing, unbinding and
+    // turning of is done
+    var cleanupIfPossible = function cleanupIfPossible() {
+      if (destroyReady && !pendingRemoval && !deferCleanup) {
+        self._cleanup();
+      }
+    };
+
+    // remove DOM element
+    if (remove && this.$el) {
+      pendingRemoval = true;
+      this.$remove(function () {
+        pendingRemoval = false;
+        cleanupIfPossible();
+      });
+    }
+
+    this._callHook('beforeDestroy');
+    this._isBeingDestroyed = true;
+    var i;
+    // remove self from parent. only necessary
+    // if parent is not being destroyed as well.
+    var parent = this.$parent;
+    if (parent && !parent._isBeingDestroyed) {
+      parent.$children.$remove(this);
+      // unregister ref (remove: true)
+      this._updateRef(true);
+    }
+    // destroy all children.
+    i = this.$children.length;
+    while (i--) {
+      this.$children[i].$destroy();
+    }
+    // teardown props
+    if (this._propsUnlinkFn) {
+      this._propsUnlinkFn();
+    }
+    // teardown all directives. this also tearsdown all
+    // directive-owned watchers.
+    if (this._unlinkFn) {
+      this._unlinkFn();
+    }
+    i = this._watchers.length;
+    while (i--) {
+      this._watchers[i].teardown();
+    }
+    // remove reference to self on $el
+    if (this.$el) {
+      this.$el.__vue__ = null;
+    }
+
+    destroyReady = true;
+    cleanupIfPossible();
+  };
+
+  /**
+   * Clean up to ensure garbage collection.
+   * This is called after the leave transition if there
+   * is any.
+   */
+
+  Vue.prototype._cleanup = function () {
+    if (this._isDestroyed) {
+      return;
+    }
+    // remove self from owner fragment
+    // do it in cleanup so that we can call $destroy with
+    // defer right when a fragment is about to be removed.
+    if (this._frag) {
+      this._frag.children.$remove(this);
+    }
+    // remove reference from data ob
+    // frozen object may not have observer.
+    if (this._data && this._data.__ob__) {
+      this._data.__ob__.removeVm(this);
+    }
+    // Clean up references to private properties and other
+    // instances. preserve reference to _data so that proxy
+    // accessors still work. The only potential side effect
+    // here is that mutating the instance after it's destroyed
+    // may affect the state of other components that are still
+    // observing the same object, but that seems to be a
+    // reasonable responsibility for the user rather than
+    // always throwing an error on them.
+    this.$el = this.$parent = this.$root = this.$children = this._watchers = this._context = this._scope = this._directives = null;
+    // call the last hook...
+    this._isDestroyed = true;
+    this._callHook('destroyed');
+    // turn off all instance listeners.
+    this.$off();
+  };
+}
+
+function miscMixin (Vue) {
+  /**
+   * Apply a list of filter (descriptors) to a value.
+   * Using plain for loops here because this will be called in
+   * the getter of any watcher with filters so it is very
+   * performance sensitive.
+   *
+   * @param {*} value
+   * @param {*} [oldValue]
+   * @param {Array} filters
+   * @param {Boolean} write
+   * @return {*}
+   */
+
+  Vue.prototype._applyFilters = function (value, oldValue, filters, write) {
+    var filter, fn, args, arg, offset, i, l, j, k;
+    for (i = 0, l = filters.length; i < l; i++) {
+      filter = filters[write ? l - i - 1 : i];
+      fn = resolveAsset(this.$options, 'filters', filter.name, true);
+      if (!fn) continue;
+      fn = write ? fn.write : fn.read || fn;
+      if (typeof fn !== 'function') continue;
+      args = write ? [value, oldValue] : [value];
+      offset = write ? 2 : 1;
+      if (filter.args) {
+        for (j = 0, k = filter.args.length; j < k; j++) {
+          arg = filter.args[j];
+          args[j + offset] = arg.dynamic ? this.$get(arg.value) : arg.value;
+        }
+      }
+      value = fn.apply(this, args);
+    }
+    return value;
+  };
+
+  /**
+   * Resolve a component, depending on whether the component
+   * is defined normally or using an async factory function.
+   * Resolves synchronously if already resolved, otherwise
+   * resolves asynchronously and caches the resolved
+   * constructor on the factory.
+   *
+   * @param {String|Function} value
+   * @param {Function} cb
+   */
+
+  Vue.prototype._resolveComponent = function (value, cb) {
+    var factory;
+    if (typeof value === 'function') {
+      factory = value;
+    } else {
+      factory = resolveAsset(this.$options, 'components', value, true);
+    }
+    /* istanbul ignore if */
+    if (!factory) {
+      return;
+    }
+    // async component factory
+    if (!factory.options) {
+      if (factory.resolved) {
+        // cached
+        cb(factory.resolved);
+      } else if (factory.requested) {
+        // pool callbacks
+        factory.pendingCallbacks.push(cb);
+      } else {
+        factory.requested = true;
+        var cbs = factory.pendingCallbacks = [cb];
+        factory.call(this, function resolve(res) {
+          if (isPlainObject(res)) {
+            res = Vue.extend(res);
+          }
+          // cache resolved
+          factory.resolved = res;
+          // invoke callbacks
+          for (var i = 0, l = cbs.length; i < l; i++) {
+            cbs[i](res);
+          }
+        }, function reject(reason) {
+          process.env.NODE_ENV !== 'production' && warn('Failed to resolve async component' + (typeof value === 'string' ? ': ' + value : '') + '. ' + (reason ? '\nReason: ' + reason : ''));
+        });
+      }
+    } else {
+      // normal component
+      cb(factory);
+    }
+  };
+}
+
+var filterRE$1 = /[^|]\|[^|]/;
+
+function dataAPI (Vue) {
+  /**
+   * Get the value from an expression on this vm.
+   *
+   * @param {String} exp
+   * @param {Boolean} [asStatement]
+   * @return {*}
+   */
+
+  Vue.prototype.$get = function (exp, asStatement) {
+    var res = parseExpression$1(exp);
+    if (res) {
+      if (asStatement) {
+        var self = this;
+        return function statementHandler() {
+          self.$arguments = toArray(arguments);
+          var result = res.get.call(self, self);
+          self.$arguments = null;
+          return result;
+        };
+      } else {
+        try {
+          return res.get.call(this, this);
+        } catch (e) {}
+      }
+    }
+  };
+
+  /**
+   * Set the value from an expression on this vm.
+   * The expression must be a valid left-hand
+   * expression in an assignment.
+   *
+   * @param {String} exp
+   * @param {*} val
+   */
+
+  Vue.prototype.$set = function (exp, val) {
+    var res = parseExpression$1(exp, true);
+    if (res && res.set) {
+      res.set.call(this, this, val);
+    }
+  };
+
+  /**
+   * Delete a property on the VM
+   *
+   * @param {String} key
+   */
+
+  Vue.prototype.$delete = function (key) {
+    del(this._data, key);
+  };
+
+  /**
+   * Watch an expression, trigger callback when its
+   * value changes.
+   *
+   * @param {String|Function} expOrFn
+   * @param {Function} cb
+   * @param {Object} [options]
+   *                 - {Boolean} deep
+   *                 - {Boolean} immediate
+   * @return {Function} - unwatchFn
+   */
+
+  Vue.prototype.$watch = function (expOrFn, cb, options) {
+    var vm = this;
+    var parsed;
+    if (typeof expOrFn === 'string') {
+      parsed = parseDirective(expOrFn);
+      expOrFn = parsed.expression;
+    }
+    var watcher = new Watcher(vm, expOrFn, cb, {
+      deep: options && options.deep,
+      sync: options && options.sync,
+      filters: parsed && parsed.filters,
+      user: !options || options.user !== false
+    });
+    if (options && options.immediate) {
+      cb.call(vm, watcher.value);
+    }
+    return function unwatchFn() {
+      watcher.teardown();
+    };
+  };
+
+  /**
+   * Evaluate a text directive, including filters.
+   *
+   * @param {String} text
+   * @param {Boolean} [asStatement]
+   * @return {String}
+   */
+
+  Vue.prototype.$eval = function (text, asStatement) {
+    // check for filters.
+    if (filterRE$1.test(text)) {
+      var dir = parseDirective(text);
+      // the filter regex check might give false positive
+      // for pipes inside strings, so it's possible that
+      // we don't get any filters here
+      var val = this.$get(dir.expression, asStatement);
+      return dir.filters ? this._applyFilters(val, null, dir.filters) : val;
+    } else {
+      // no filter
+      return this.$get(text, asStatement);
+    }
+  };
+
+  /**
+   * Interpolate a piece of template text.
+   *
+   * @param {String} text
+   * @return {String}
+   */
+
+  Vue.prototype.$interpolate = function (text) {
+    var tokens = parseText(text);
+    var vm = this;
+    if (tokens) {
+      if (tokens.length === 1) {
+        return vm.$eval(tokens[0].value) + '';
+      } else {
+        return tokens.map(function (token) {
+          return token.tag ? vm.$eval(token.value) : token.value;
+        }).join('');
+      }
+    } else {
+      return text;
+    }
+  };
+
+  /**
+   * Log instance data as a plain JS object
+   * so that it is easier to inspect in console.
+   * This method assumes console is available.
+   *
+   * @param {String} [path]
+   */
+
+  Vue.prototype.$log = function (path) {
+    var data = path ? getPath(this._data, path) : this._data;
+    if (data) {
+      data = clean(data);
+    }
+    // include computed fields
+    if (!path) {
+      var key;
+      for (key in this.$options.computed) {
+        data[key] = clean(this[key]);
+      }
+      if (this._props) {
+        for (key in this._props) {
+          data[key] = clean(this[key]);
+        }
+      }
+    }
+    console.log(data);
+  };
+
+  /**
+   * "clean" a getter/setter converted object into a plain
+   * object copy.
+   *
+   * @param {Object} - obj
+   * @return {Object}
+   */
+
+  function clean(obj) {
+    return JSON.parse(JSON.stringify(obj));
+  }
+}
+
+function domAPI (Vue) {
+  /**
+   * Convenience on-instance nextTick. The callback is
+   * auto-bound to the instance, and this avoids component
+   * modules having to rely on the global Vue.
+   *
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$nextTick = function (fn) {
+    nextTick(fn, this);
+  };
+
+  /**
+   * Append instance to target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$appendTo = function (target, cb, withTransition) {
+    return insert(this, target, cb, withTransition, append, appendWithTransition);
+  };
+
+  /**
+   * Prepend instance to target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$prependTo = function (target, cb, withTransition) {
+    target = query(target);
+    if (target.hasChildNodes()) {
+      this.$before(target.firstChild, cb, withTransition);
+    } else {
+      this.$appendTo(target, cb, withTransition);
+    }
+    return this;
+  };
+
+  /**
+   * Insert instance before target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$before = function (target, cb, withTransition) {
+    return insert(this, target, cb, withTransition, beforeWithCb, beforeWithTransition);
+  };
+
+  /**
+   * Insert instance after target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$after = function (target, cb, withTransition) {
+    target = query(target);
+    if (target.nextSibling) {
+      this.$before(target.nextSibling, cb, withTransition);
+    } else {
+      this.$appendTo(target.parentNode, cb, withTransition);
+    }
+    return this;
+  };
+
+  /**
+   * Remove instance from DOM
+   *
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$remove = function (cb, withTransition) {
+    if (!this.$el.parentNode) {
+      return cb && cb();
+    }
+    var inDocument = this._isAttached && inDoc(this.$el);
+    // if we are not in document, no need to check
+    // for transitions
+    if (!inDocument) withTransition = false;
+    var self = this;
+    var realCb = function realCb() {
+      if (inDocument) self._callHook('detached');
+      if (cb) cb();
+    };
+    if (this._isFragment) {
+      removeNodeRange(this._fragmentStart, this._fragmentEnd, this, this._fragment, realCb);
+    } else {
+      var op = withTransition === false ? removeWithCb : removeWithTransition;
+      op(this.$el, this, realCb);
+    }
+    return this;
+  };
+
+  /**
+   * Shared DOM insertion function.
+   *
+   * @param {Vue} vm
+   * @param {Element} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition]
+   * @param {Function} op1 - op for non-transition insert
+   * @param {Function} op2 - op for transition insert
+   * @return vm
+   */
+
+  function insert(vm, target, cb, withTransition, op1, op2) {
+    target = query(target);
+    var targetIsDetached = !inDoc(target);
+    var op = withTransition === false || targetIsDetached ? op1 : op2;
+    var shouldCallHook = !targetIsDetached && !vm._isAttached && !inDoc(vm.$el);
+    if (vm._isFragment) {
+      mapNodeRange(vm._fragmentStart, vm._fragmentEnd, function (node) {
+        op(node, target, vm);
+      });
+      cb && cb();
+    } else {
+      op(vm.$el, target, vm, cb);
+    }
+    if (shouldCallHook) {
+      vm._callHook('attached');
+    }
+    return vm;
+  }
+
+  /**
+   * Check for selectors
+   *
+   * @param {String|Element} el
+   */
+
+  function query(el) {
+    return typeof el === 'string' ? document.querySelector(el) : el;
+  }
+
+  /**
+   * Append operation that takes a callback.
+   *
+   * @param {Node} el
+   * @param {Node} target
+   * @param {Vue} vm - unused
+   * @param {Function} [cb]
+   */
+
+  function append(el, target, vm, cb) {
+    target.appendChild(el);
+    if (cb) cb();
+  }
+
+  /**
+   * InsertBefore operation that takes a callback.
+   *
+   * @param {Node} el
+   * @param {Node} target
+   * @param {Vue} vm - unused
+   * @param {Function} [cb]
+   */
+
+  function beforeWithCb(el, target, vm, cb) {
+    before(el, target);
+    if (cb) cb();
+  }
+
+  /**
+   * Remove operation that takes a callback.
+   *
+   * @param {Node} el
+   * @param {Vue} vm - unused
+   * @param {Function} [cb]
+   */
+
+  function removeWithCb(el, vm, cb) {
+    remove(el);
+    if (cb) cb();
+  }
+}
+
+function eventsAPI (Vue) {
+  /**
+   * Listen on the given `event` with `fn`.
+   *
+   * @param {String} event
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$on = function (event, fn) {
+    (this._events[event] || (this._events[event] = [])).push(fn);
+    modifyListenerCount(this, event, 1);
+    return this;
+  };
+
+  /**
+   * Adds an `event` listener that will be invoked a single
+   * time then automatically removed.
+   *
+   * @param {String} event
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$once = function (event, fn) {
+    var self = this;
+    function on() {
+      self.$off(event, on);
+      fn.apply(this, arguments);
+    }
+    on.fn = fn;
+    this.$on(event, on);
+    return this;
+  };
+
+  /**
+   * Remove the given callback for `event` or all
+   * registered callbacks.
+   *
+   * @param {String} event
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$off = function (event, fn) {
+    var cbs;
+    // all
+    if (!arguments.length) {
+      if (this.$parent) {
+        for (event in this._events) {
+          cbs = this._events[event];
+          if (cbs) {
+            modifyListenerCount(this, event, -cbs.length);
+          }
+        }
+      }
+      this._events = {};
+      return this;
+    }
+    // specific event
+    cbs = this._events[event];
+    if (!cbs) {
+      return this;
+    }
+    if (arguments.length === 1) {
+      modifyListenerCount(this, event, -cbs.length);
+      this._events[event] = null;
+      return this;
+    }
+    // specific handler
+    var cb;
+    var i = cbs.length;
+    while (i--) {
+      cb = cbs[i];
+      if (cb === fn || cb.fn === fn) {
+        modifyListenerCount(this, event, -1);
+        cbs.splice(i, 1);
+        break;
+      }
+    }
+    return this;
+  };
+
+  /**
+   * Trigger an event on self.
+   *
+   * @param {String|Object} event
+   * @return {Boolean} shouldPropagate
+   */
+
+  Vue.prototype.$emit = function (event) {
+    var isSource = typeof event === 'string';
+    event = isSource ? event : event.name;
+    var cbs = this._events[event];
+    var shouldPropagate = isSource || !cbs;
+    if (cbs) {
+      cbs = cbs.length > 1 ? toArray(cbs) : cbs;
+      // this is a somewhat hacky solution to the question raised
+      // in #2102: for an inline component listener like <comp @test="doThis">,
+      // the propagation handling is somewhat broken. Therefore we
+      // need to treat these inline callbacks differently.
+      var hasParentCbs = isSource && cbs.some(function (cb) {
+        return cb._fromParent;
+      });
+      if (hasParentCbs) {
+        shouldPropagate = false;
+      }
+      var args = toArray(arguments, 1);
+      for (var i = 0, l = cbs.length; i < l; i++) {
+        var cb = cbs[i];
+        var res = cb.apply(this, args);
+        if (res === true && (!hasParentCbs || cb._fromParent)) {
+          shouldPropagate = true;
+        }
+      }
+    }
+    return shouldPropagate;
+  };
+
+  /**
+   * Recursively broadcast an event to all children instances.
+   *
+   * @param {String|Object} event
+   * @param {...*} additional arguments
+   */
+
+  Vue.prototype.$broadcast = function (event) {
+    var isSource = typeof event === 'string';
+    event = isSource ? event : event.name;
+    // if no child has registered for this event,
+    // then there's no need to broadcast.
+    if (!this._eventsCount[event]) return;
+    var children = this.$children;
+    var args = toArray(arguments);
+    if (isSource) {
+      // use object event to indicate non-source emit
+      // on children
+      args[0] = { name: event, source: this };
+    }
+    for (var i = 0, l = children.length; i < l; i++) {
+      var child = children[i];
+      var shouldPropagate = child.$emit.apply(child, args);
+      if (shouldPropagate) {
+        child.$broadcast.apply(child, args);
+      }
+    }
+    return this;
+  };
+
+  /**
+   * Recursively propagate an event up the parent chain.
+   *
+   * @param {String} event
+   * @param {...*} additional arguments
+   */
+
+  Vue.prototype.$dispatch = function (event) {
+    var shouldPropagate = this.$emit.apply(this, arguments);
+    if (!shouldPropagate) return;
+    var parent = this.$parent;
+    var args = toArray(arguments);
+    // use object event to indicate non-source emit
+    // on parents
+    args[0] = { name: event, source: this };
+    while (parent) {
+      shouldPropagate = parent.$emit.apply(parent, args);
+      parent = shouldPropagate ? parent.$parent : null;
+    }
+    return this;
+  };
+
+  /**
+   * Modify the listener counts on all parents.
+   * This bookkeeping allows $broadcast to return early when
+   * no child has listened to a certain event.
+   *
+   * @param {Vue} vm
+   * @param {String} event
+   * @param {Number} count
+   */
+
+  var hookRE = /^hook:/;
+  function modifyListenerCount(vm, event, count) {
+    var parent = vm.$parent;
+    // hooks do not get broadcasted so no need
+    // to do bookkeeping for them
+    if (!parent || !count || hookRE.test(event)) return;
+    while (parent) {
+      parent._eventsCount[event] = (parent._eventsCount[event] || 0) + count;
+      parent = parent.$parent;
+    }
+  }
+}
+
+function lifecycleAPI (Vue) {
+  /**
+   * Set instance target element and kick off the compilation
+   * process. The passed in `el` can be a selector string, an
+   * existing Element, or a DocumentFragment (for block
+   * instances).
+   *
+   * @param {Element|DocumentFragment|string} el
+   * @public
+   */
+
+  Vue.prototype.$mount = function (el) {
+    if (this._isCompiled) {
+      process.env.NODE_ENV !== 'production' && warn('$mount() should be called only once.', this);
+      return;
+    }
+    el = query(el);
+    if (!el) {
+      el = document.createElement('div');
+    }
+    this._compile(el);
+    this._initDOMHooks();
+    if (inDoc(this.$el)) {
+      this._callHook('attached');
+      ready.call(this);
+    } else {
+      this.$once('hook:attached', ready);
+    }
+    return this;
+  };
+
+  /**
+   * Mark an instance as ready.
+   */
+
+  function ready() {
+    this._isAttached = true;
+    this._isReady = true;
+    this._callHook('ready');
+  }
+
+  /**
+   * Teardown the instance, simply delegate to the internal
+   * _destroy.
+   *
+   * @param {Boolean} remove
+   * @param {Boolean} deferCleanup
+   */
+
+  Vue.prototype.$destroy = function (remove, deferCleanup) {
+    this._destroy(remove, deferCleanup);
+  };
+
+  /**
+   * Partially compile a piece of DOM and return a
+   * decompile function.
+   *
+   * @param {Element|DocumentFragment} el
+   * @param {Vue} [host]
+   * @param {Object} [scope]
+   * @param {Fragment} [frag]
+   * @return {Function}
+   */
+
+  Vue.prototype.$compile = function (el, host, scope, frag) {
+    return compile(el, this.$options, true)(this, el, host, scope, frag);
+  };
+}
+
+/**
+ * The exposed Vue constructor.
+ *
+ * API conventions:
+ * - public API methods/properties are prefixed with `$`
+ * - internal methods/properties are prefixed with `_`
+ * - non-prefixed properties are assumed to be proxied user
+ *   data.
+ *
+ * @constructor
+ * @param {Object} [options]
+ * @public
+ */
+
+function Vue(options) {
+  this._init(options);
+}
+
+// install internals
+initMixin(Vue);
+stateMixin(Vue);
+eventsMixin(Vue);
+lifecycleMixin(Vue);
+miscMixin(Vue);
+
+// install instance APIs
+dataAPI(Vue);
+domAPI(Vue);
+eventsAPI(Vue);
+lifecycleAPI(Vue);
+
+var slot = {
+
+  priority: SLOT,
+  params: ['name'],
+
+  bind: function bind() {
+    // this was resolved during component transclusion
+    var name = this.params.name || 'default';
+    var content = this.vm._slotContents && this.vm._slotContents[name];
+    if (!content || !content.hasChildNodes()) {
+      this.fallback();
+    } else {
+      this.compile(content.cloneNode(true), this.vm._context, this.vm);
+    }
+  },
+
+  compile: function compile(content, context, host) {
+    if (content && context) {
+      if (this.el.hasChildNodes() && content.childNodes.length === 1 && content.childNodes[0].nodeType === 1 && content.childNodes[0].hasAttribute('v-if')) {
+        // if the inserted slot has v-if
+        // inject fallback content as the v-else
+        var elseBlock = document.createElement('template');
+        elseBlock.setAttribute('v-else', '');
+        elseBlock.innerHTML = this.el.innerHTML;
+        // the else block should be compiled in child scope
+        elseBlock._context = this.vm;
+        content.appendChild(elseBlock);
+      }
+      var scope = host ? host._scope : this._scope;
+      this.unlink = context.$compile(content, host, scope, this._frag);
+    }
+    if (content) {
+      replace(this.el, content);
+    } else {
+      remove(this.el);
+    }
+  },
+
+  fallback: function fallback() {
+    this.compile(extractContent(this.el, true), this.vm);
+  },
+
+  unbind: function unbind() {
+    if (this.unlink) {
+      this.unlink();
+    }
+  }
+};
+
+var partial = {
+
+  priority: PARTIAL,
+
+  params: ['name'],
+
+  // watch changes to name for dynamic partials
+  paramWatchers: {
+    name: function name(value) {
+      vIf.remove.call(this);
+      if (value) {
+        this.insert(value);
+      }
+    }
+  },
+
+  bind: function bind() {
+    this.anchor = createAnchor('v-partial');
+    replace(this.el, this.anchor);
+    this.insert(this.params.name);
+  },
+
+  insert: function insert(id) {
+    var partial = resolveAsset(this.vm.$options, 'partials', id, true);
+    if (partial) {
+      this.factory = new FragmentFactory(this.vm, partial);
+      vIf.insert.call(this);
+    }
+  },
+
+  unbind: function unbind() {
+    if (this.frag) {
+      this.frag.destroy();
+    }
+  }
+};
+
+var elementDirectives = {
+  slot: slot,
+  partial: partial
+};
+
+var convertArray = vFor._postProcess;
+
+/**
+ * Limit filter for arrays
+ *
+ * @param {Number} n
+ * @param {Number} offset (Decimal expected)
+ */
+
+function limitBy(arr, n, offset) {
+  offset = offset ? parseInt(offset, 10) : 0;
+  n = toNumber(n);
+  return typeof n === 'number' ? arr.slice(offset, offset + n) : arr;
+}
+
+/**
+ * Filter filter for arrays
+ *
+ * @param {String} search
+ * @param {String} [delimiter]
+ * @param {String} ...dataKeys
+ */
+
+function filterBy(arr, search, delimiter) {
+  arr = convertArray(arr);
+  if (search == null) {
+    return arr;
+  }
+  if (typeof search === 'function') {
+    return arr.filter(search);
+  }
+  // cast to lowercase string
+  search = ('' + search).toLowerCase();
+  // allow optional `in` delimiter
+  // because why not
+  var n = delimiter === 'in' ? 3 : 2;
+  // extract and flatten keys
+  var keys = Array.prototype.concat.apply([], toArray(arguments, n));
+  var res = [];
+  var item, key, val, j;
+  for (var i = 0, l = arr.length; i < l; i++) {
+    item = arr[i];
+    val = item && item.$value || item;
+    j = keys.length;
+    if (j) {
+      while (j--) {
+        key = keys[j];
+        if (key === '$key' && contains(item.$key, search) || contains(getPath(val, key), search)) {
+          res.push(item);
+          break;
+        }
+      }
+    } else if (contains(item, search)) {
+      res.push(item);
+    }
+  }
+  return res;
+}
+
+/**
+ * Order filter for arrays
+ *
+ * @param {String|Array<String>|Function} ...sortKeys
+ * @param {Number} [order]
+ */
+
+function orderBy(arr) {
+  var comparator = null;
+  var sortKeys = undefined;
+  arr = convertArray(arr);
+
+  // determine order (last argument)
+  var args = toArray(arguments, 1);
+  var order = args[args.length - 1];
+  if (typeof order === 'number') {
+    order = order < 0 ? -1 : 1;
+    args = args.length > 1 ? args.slice(0, -1) : args;
+  } else {
+    order = 1;
+  }
+
+  // determine sortKeys & comparator
+  var firstArg = args[0];
+  if (!firstArg) {
+    return arr;
+  } else if (typeof firstArg === 'function') {
+    // custom comparator
+    comparator = function (a, b) {
+      return firstArg(a, b) * order;
+    };
+  } else {
+    // string keys. flatten first
+    sortKeys = Array.prototype.concat.apply([], args);
+    comparator = function (a, b, i) {
+      i = i || 0;
+      return i >= sortKeys.length - 1 ? baseCompare(a, b, i) : baseCompare(a, b, i) || comparator(a, b, i + 1);
+    };
+  }
+
+  function baseCompare(a, b, sortKeyIndex) {
+    var sortKey = sortKeys[sortKeyIndex];
+    if (sortKey) {
+      if (sortKey !== '$key') {
+        if (isObject(a) && '$value' in a) a = a.$value;
+        if (isObject(b) && '$value' in b) b = b.$value;
+      }
+      a = isObject(a) ? getPath(a, sortKey) : a;
+      b = isObject(b) ? getPath(b, sortKey) : b;
+    }
+    return a === b ? 0 : a > b ? order : -order;
+  }
+
+  // sort on a copy to avoid mutating original array
+  return arr.slice().sort(comparator);
+}
+
+/**
+ * String contain helper
+ *
+ * @param {*} val
+ * @param {String} search
+ */
+
+function contains(val, search) {
+  var i;
+  if (isPlainObject(val)) {
+    var keys = Object.keys(val);
+    i = keys.length;
+    while (i--) {
+      if (contains(val[keys[i]], search)) {
+        return true;
+      }
+    }
+  } else if (isArray(val)) {
+    i = val.length;
+    while (i--) {
+      if (contains(val[i], search)) {
+        return true;
+      }
+    }
+  } else if (val != null) {
+    return val.toString().toLowerCase().indexOf(search) > -1;
+  }
+}
+
+var digitsRE = /(\d{3})(?=\d)/g;
+
+// asset collections must be a plain object.
+var filters = {
+
+  orderBy: orderBy,
+  filterBy: filterBy,
+  limitBy: limitBy,
+
+  /**
+   * Stringify value.
+   *
+   * @param {Number} indent
+   */
+
+  json: {
+    read: function read(value, indent) {
+      return typeof value === 'string' ? value : JSON.stringify(value, null, arguments.length > 1 ? indent : 2);
+    },
+    write: function write(value) {
+      try {
+        return JSON.parse(value);
+      } catch (e) {
+        return value;
+      }
+    }
+  },
+
+  /**
+   * 'abc' => 'Abc'
+   */
+
+  capitalize: function capitalize(value) {
+    if (!value && value !== 0) return '';
+    value = value.toString();
+    return value.charAt(0).toUpperCase() + value.slice(1);
+  },
+
+  /**
+   * 'abc' => 'ABC'
+   */
+
+  uppercase: function uppercase(value) {
+    return value || value === 0 ? value.toString().toUpperCase() : '';
+  },
+
+  /**
+   * 'AbC' => 'abc'
+   */
+
+  lowercase: function lowercase(value) {
+    return value || value === 0 ? value.toString().toLowerCase() : '';
+  },
+
+  /**
+   * 12345 => $12,345.00
+   *
+   * @param {String} sign
+   * @param {Number} decimals Decimal places
+   */
+
+  currency: function currency(value, _currency, decimals) {
+    value = parseFloat(value);
+    if (!isFinite(value) || !value && value !== 0) return '';
+    _currency = _currency != null ? _currency : '$';
+    decimals = decimals != null ? decimals : 2;
+    var stringified = Math.abs(value).toFixed(decimals);
+    var _int = decimals ? stringified.slice(0, -1 - decimals) : stringified;
+    var i = _int.length % 3;
+    var head = i > 0 ? _int.slice(0, i) + (_int.length > 3 ? ',' : '') : '';
+    var _float = decimals ? stringified.slice(-1 - decimals) : '';
+    var sign = value < 0 ? '-' : '';
+    return sign + _currency + head + _int.slice(i).replace(digitsRE, '$1,') + _float;
+  },
+
+  /**
+   * 'item' => 'items'
+   *
+   * @params
+   *  an array of strings corresponding to
+   *  the single, double, triple ... forms of the word to
+   *  be pluralized. When the number to be pluralized
+   *  exceeds the length of the args, it will use the last
+   *  entry in the array.
+   *
+   *  e.g. ['single', 'double', 'triple', 'multiple']
+   */
+
+  pluralize: function pluralize(value) {
+    var args = toArray(arguments, 1);
+    var length = args.length;
+    if (length > 1) {
+      var index = value % 10 - 1;
+      return index in args ? args[index] : args[length - 1];
+    } else {
+      return args[0] + (value === 1 ? '' : 's');
+    }
+  },
+
+  /**
+   * Debounce a handler function.
+   *
+   * @param {Function} handler
+   * @param {Number} delay = 300
+   * @return {Function}
+   */
+
+  debounce: function debounce(handler, delay) {
+    if (!handler) return;
+    if (!delay) {
+      delay = 300;
+    }
+    return _debounce(handler, delay);
+  }
+};
+
+function installGlobalAPI (Vue) {
+  /**
+   * Vue and every constructor that extends Vue has an
+   * associated options object, which can be accessed during
+   * compilation steps as `this.constructor.options`.
+   *
+   * These can be seen as the default options of every
+   * Vue instance.
+   */
+
+  Vue.options = {
+    directives: directives,
+    elementDirectives: elementDirectives,
+    filters: filters,
+    transitions: {},
+    components: {},
+    partials: {},
+    replace: true
+  };
+
+  /**
+   * Expose useful internals
+   */
+
+  Vue.util = util;
+  Vue.config = config;
+  Vue.set = set;
+  Vue['delete'] = del;
+  Vue.nextTick = nextTick;
+
+  /**
+   * The following are exposed for advanced usage / plugins
+   */
+
+  Vue.compiler = compiler;
+  Vue.FragmentFactory = FragmentFactory;
+  Vue.internalDirectives = internalDirectives;
+  Vue.parsers = {
+    path: path,
+    text: text,
+    template: template,
+    directive: directive,
+    expression: expression
+  };
+
+  /**
+   * Each instance constructor, including Vue, has a unique
+   * cid. This enables us to create wrapped "child
+   * constructors" for prototypal inheritance and cache them.
+   */
+
+  Vue.cid = 0;
+  var cid = 1;
+
+  /**
+   * Class inheritance
+   *
+   * @param {Object} extendOptions
+   */
+
+  Vue.extend = function (extendOptions) {
+    extendOptions = extendOptions || {};
+    var Super = this;
+    var isFirstExtend = Super.cid === 0;
+    if (isFirstExtend && extendOptions._Ctor) {
+      return extendOptions._Ctor;
+    }
+    var name = extendOptions.name || Super.options.name;
+    if (process.env.NODE_ENV !== 'production') {
+      if (!/^[a-zA-Z][\w-]*$/.test(name)) {
+        warn('Invalid component name: "' + name + '". Component names ' + 'can only contain alphanumeric characaters and the hyphen.');
+        name = null;
+      }
+    }
+    var Sub = createClass(name || 'VueComponent');
+    Sub.prototype = Object.create(Super.prototype);
+    Sub.prototype.constructor = Sub;
+    Sub.cid = cid++;
+    Sub.options = mergeOptions(Super.options, extendOptions);
+    Sub['super'] = Super;
+    // allow further extension
+    Sub.extend = Super.extend;
+    // create asset registers, so extended classes
+    // can have their private assets too.
+    config._assetTypes.forEach(function (type) {
+      Sub[type] = Super[type];
+    });
+    // enable recursive self-lookup
+    if (name) {
+      Sub.options.components[name] = Sub;
+    }
+    // cache constructor
+    if (isFirstExtend) {
+      extendOptions._Ctor = Sub;
+    }
+    return Sub;
+  };
+
+  /**
+   * A function that returns a sub-class constructor with the
+   * given name. This gives us much nicer output when
+   * logging instances in the console.
+   *
+   * @param {String} name
+   * @return {Function}
+   */
+
+  function createClass(name) {
+    /* eslint-disable no-new-func */
+    return new Function('return function ' + classify(name) + ' (options) { this._init(options) }')();
+    /* eslint-enable no-new-func */
+  }
+
+  /**
+   * Plugin system
+   *
+   * @param {Object} plugin
+   */
+
+  Vue.use = function (plugin) {
+    /* istanbul ignore if */
+    if (plugin.installed) {
+      return;
+    }
+    // additional parameters
+    var args = toArray(arguments, 1);
+    args.unshift(this);
+    if (typeof plugin.install === 'function') {
+      plugin.install.apply(plugin, args);
+    } else {
+      plugin.apply(null, args);
+    }
+    plugin.installed = true;
+    return this;
+  };
+
+  /**
+   * Apply a global mixin by merging it into the default
+   * options.
+   */
+
+  Vue.mixin = function (mixin) {
+    Vue.options = mergeOptions(Vue.options, mixin);
+  };
+
+  /**
+   * Create asset registration methods with the following
+   * signature:
+   *
+   * @param {String} id
+   * @param {*} definition
+   */
+
+  config._assetTypes.forEach(function (type) {
+    Vue[type] = function (id, definition) {
+      if (!definition) {
+        return this.options[type + 's'][id];
+      } else {
+        /* istanbul ignore if */
+        if (process.env.NODE_ENV !== 'production') {
+          if (type === 'component' && (commonTagRE.test(id) || reservedTagRE.test(id))) {
+            warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + id);
+          }
+        }
+        if (type === 'component' && isPlainObject(definition)) {
+          if (!definition.name) {
+            definition.name = id;
+          }
+          definition = Vue.extend(definition);
+        }
+        this.options[type + 's'][id] = definition;
+        return definition;
+      }
+    };
+  });
+
+  // expose internal transition API
+  extend(Vue.transition, transition);
 }
+
+installGlobalAPI(Vue);
+
+Vue.version = '1.0.28';
+
+// devtools global hook
+/* istanbul ignore next */
+setTimeout(function () {
+  if (config.devtools) {
+    if (devtools) {
+      devtools.emit('init', Vue);
+    } else if (process.env.NODE_ENV !== 'production' && inBrowser && /Chrome\/\d+/.test(window.navigator.userAgent)) {
+      console.log('Download the Vue Devtools for a better development experience:\n' + 'https://github.com/vuejs/vue-devtools');
+    }
+  }
+}, 0);
+
+module.exports = Vue;
\ No newline at end of file
diff --git a/dist/vue.js b/dist/vue.js
new file mode 100644
index 00000000000..7b3ea3e94a1
--- /dev/null
+++ b/dist/vue.js
@@ -0,0 +1,10237 @@
+/*!
+ * Vue.js v1.0.28
+ * (c) 2016 Evan You
+ * Released under the MIT License.
+ */
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+  typeof define === 'function' && define.amd ? define(factory) :
+  (global.Vue = factory());
+}(this, (function () { 'use strict';
+
+function set(obj, key, val) {
+  if (hasOwn(obj, key)) {
+    obj[key] = val;
+    return;
+  }
+  if (obj._isVue) {
+    set(obj._data, key, val);
+    return;
+  }
+  var ob = obj.__ob__;
+  if (!ob) {
+    obj[key] = val;
+    return;
+  }
+  ob.convert(key, val);
+  ob.dep.notify();
+  if (ob.vms) {
+    var i = ob.vms.length;
+    while (i--) {
+      var vm = ob.vms[i];
+      vm._proxy(key);
+      vm._digest();
+    }
+  }
+  return val;
+}
+
+/**
+ * Delete a property and trigger change if necessary.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ */
+
+function del(obj, key) {
+  if (!hasOwn(obj, key)) {
+    return;
+  }
+  delete obj[key];
+  var ob = obj.__ob__;
+  if (!ob) {
+    if (obj._isVue) {
+      delete obj._data[key];
+      obj._digest();
+    }
+    return;
+  }
+  ob.dep.notify();
+  if (ob.vms) {
+    var i = ob.vms.length;
+    while (i--) {
+      var vm = ob.vms[i];
+      vm._unproxy(key);
+      vm._digest();
+    }
+  }
+}
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+/**
+ * Check whether the object has the property.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @return {Boolean}
+ */
+
+function hasOwn(obj, key) {
+  return hasOwnProperty.call(obj, key);
+}
+
+/**
+ * Check if an expression is a literal value.
+ *
+ * @param {String} exp
+ * @return {Boolean}
+ */
+
+var literalValueRE = /^\s?(true|false|-?[\d\.]+|'[^']*'|"[^"]*")\s?$/;
+
+function isLiteral(exp) {
+  return literalValueRE.test(exp);
+}
+
+/**
+ * Check if a string starts with $ or _
+ *
+ * @param {String} str
+ * @return {Boolean}
+ */
+
+function isReserved(str) {
+  var c = (str + '').charCodeAt(0);
+  return c === 0x24 || c === 0x5F;
+}
+
+/**
+ * Guard text output, make sure undefined outputs
+ * empty string
+ *
+ * @param {*} value
+ * @return {String}
+ */
+
+function _toString(value) {
+  return value == null ? '' : value.toString();
+}
+
+/**
+ * Check and convert possible numeric strings to numbers
+ * before setting back to data
+ *
+ * @param {*} value
+ * @return {*|Number}
+ */
+
+function toNumber(value) {
+  if (typeof value !== 'string') {
+    return value;
+  } else {
+    var parsed = Number(value);
+    return isNaN(parsed) ? value : parsed;
+  }
+}
+
+/**
+ * Convert string boolean literals into real booleans.
+ *
+ * @param {*} value
+ * @return {*|Boolean}
+ */
+
+function toBoolean(value) {
+  return value === 'true' ? true : value === 'false' ? false : value;
+}
+
+/**
+ * Strip quotes from a string
+ *
+ * @param {String} str
+ * @return {String | false}
+ */
+
+function stripQuotes(str) {
+  var a = str.charCodeAt(0);
+  var b = str.charCodeAt(str.length - 1);
+  return a === b && (a === 0x22 || a === 0x27) ? str.slice(1, -1) : str;
+}
+
+/**
+ * Camelize a hyphen-delimited string.
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+var camelizeRE = /-(\w)/g;
+
+function camelize(str) {
+  return str.replace(camelizeRE, toUpper);
+}
+
+function toUpper(_, c) {
+  return c ? c.toUpperCase() : '';
+}
+
+/**
+ * Hyphenate a camelCase string.
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+var hyphenateRE = /([^-])([A-Z])/g;
+
+function hyphenate(str) {
+  return str.replace(hyphenateRE, '$1-$2').replace(hyphenateRE, '$1-$2').toLowerCase();
+}
+
+/**
+ * Converts hyphen/underscore/slash delimitered names into
+ * camelized classNames.
+ *
+ * e.g. my-component => MyComponent
+ *      some_else    => SomeElse
+ *      some/comp    => SomeComp
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+var classifyRE = /(?:^|[-_\/])(\w)/g;
+
+function classify(str) {
+  return str.replace(classifyRE, toUpper);
+}
+
+/**
+ * Simple bind, faster than native
+ *
+ * @param {Function} fn
+ * @param {Object} ctx
+ * @return {Function}
+ */
+
+function bind(fn, ctx) {
+  return function (a) {
+    var l = arguments.length;
+    return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx);
+  };
+}
+
+/**
+ * Convert an Array-like object to a real Array.
+ *
+ * @param {Array-like} list
+ * @param {Number} [start] - start index
+ * @return {Array}
+ */
+
+function toArray(list, start) {
+  start = start || 0;
+  var i = list.length - start;
+  var ret = new Array(i);
+  while (i--) {
+    ret[i] = list[i + start];
+  }
+  return ret;
+}
+
+/**
+ * Mix properties into target object.
+ *
+ * @param {Object} to
+ * @param {Object} from
+ */
+
+function extend(to, from) {
+  var keys = Object.keys(from);
+  var i = keys.length;
+  while (i--) {
+    to[keys[i]] = from[keys[i]];
+  }
+  return to;
+}
+
+/**
+ * Quick object check - this is primarily used to tell
+ * Objects from primitive values when we know the value
+ * is a JSON-compliant type.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+function isObject(obj) {
+  return obj !== null && typeof obj === 'object';
+}
+
+/**
+ * Strict object type check. Only returns true
+ * for plain JavaScript objects.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+var toString = Object.prototype.toString;
+var OBJECT_STRING = '[object Object]';
+
+function isPlainObject(obj) {
+  return toString.call(obj) === OBJECT_STRING;
+}
+
+/**
+ * Array type check.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+var isArray = Array.isArray;
+
+/**
+ * Define a property.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {*} val
+ * @param {Boolean} [enumerable]
+ */
+
+function def(obj, key, val, enumerable) {
+  Object.defineProperty(obj, key, {
+    value: val,
+    enumerable: !!enumerable,
+    writable: true,
+    configurable: true
+  });
+}
+
+/**
+ * Debounce a function so it only gets called after the
+ * input stops arriving after the given wait period.
+ *
+ * @param {Function} func
+ * @param {Number} wait
+ * @return {Function} - the debounced function
+ */
+
+function _debounce(func, wait) {
+  var timeout, args, context, timestamp, result;
+  var later = function later() {
+    var last = Date.now() - timestamp;
+    if (last < wait && last >= 0) {
+      timeout = setTimeout(later, wait - last);
+    } else {
+      timeout = null;
+      result = func.apply(context, args);
+      if (!timeout) context = args = null;
+    }
+  };
+  return function () {
+    context = this;
+    args = arguments;
+    timestamp = Date.now();
+    if (!timeout) {
+      timeout = setTimeout(later, wait);
+    }
+    return result;
+  };
+}
+
+/**
+ * Manual indexOf because it's slightly faster than
+ * native.
+ *
+ * @param {Array} arr
+ * @param {*} obj
+ */
+
+function indexOf(arr, obj) {
+  var i = arr.length;
+  while (i--) {
+    if (arr[i] === obj) return i;
+  }
+  return -1;
+}
+
+/**
+ * Make a cancellable version of an async callback.
+ *
+ * @param {Function} fn
+ * @return {Function}
+ */
+
+function cancellable(fn) {
+  var cb = function cb() {
+    if (!cb.cancelled) {
+      return fn.apply(this, arguments);
+    }
+  };
+  cb.cancel = function () {
+    cb.cancelled = true;
+  };
+  return cb;
+}
+
+/**
+ * Check if two values are loosely equal - that is,
+ * if they are plain objects, do they have the same shape?
+ *
+ * @param {*} a
+ * @param {*} b
+ * @return {Boolean}
+ */
+
+function looseEqual(a, b) {
+  /* eslint-disable eqeqeq */
+  return a == b || (isObject(a) && isObject(b) ? JSON.stringify(a) === JSON.stringify(b) : false);
+  /* eslint-enable eqeqeq */
+}
+
+var hasProto = ('__proto__' in {});
+
+// Browser environment sniffing
+var inBrowser = typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]';
+
+// detect devtools
+var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
+
+// UA sniffing for working around browser-specific quirks
+var UA = inBrowser && window.navigator.userAgent.toLowerCase();
+var isIE = UA && UA.indexOf('trident') > 0;
+var isIE9 = UA && UA.indexOf('msie 9.0') > 0;
+var isAndroid = UA && UA.indexOf('android') > 0;
+var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA);
+
+var transitionProp = undefined;
+var transitionEndEvent = undefined;
+var animationProp = undefined;
+var animationEndEvent = undefined;
+
+// Transition property/event sniffing
+if (inBrowser && !isIE9) {
+  var isWebkitTrans = window.ontransitionend === undefined && window.onwebkittransitionend !== undefined;
+  var isWebkitAnim = window.onanimationend === undefined && window.onwebkitanimationend !== undefined;
+  transitionProp = isWebkitTrans ? 'WebkitTransition' : 'transition';
+  transitionEndEvent = isWebkitTrans ? 'webkitTransitionEnd' : 'transitionend';
+  animationProp = isWebkitAnim ? 'WebkitAnimation' : 'animation';
+  animationEndEvent = isWebkitAnim ? 'webkitAnimationEnd' : 'animationend';
+}
+
+/* istanbul ignore next */
+function isNative(Ctor) {
+  return (/native code/.test(Ctor.toString())
+  );
+}
+
+/**
+ * Defer a task to execute it asynchronously. Ideally this
+ * should be executed as a microtask, so we leverage
+ * MutationObserver if it's available, and fallback to
+ * setTimeout(0).
+ *
+ * @param {Function} cb
+ * @param {Object} ctx
+ */
+
+var nextTick = (function () {
+  var callbacks = [];
+  var pending = false;
+  var timerFunc = undefined;
+
+  function nextTickHandler() {
+    pending = false;
+    var copies = callbacks.slice(0);
+    callbacks.length = 0;
+    for (var i = 0; i < copies.length; i++) {
+      copies[i]();
+    }
+  }
+
+  // the nextTick behavior leverages the microtask queue, which can be accessed
+  // via either native Promise.then or MutationObserver.
+  // MutationObserver has wider support, however it is seriously bugged in
+  // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
+  // completely stops working after triggering a few times... so, if native
+  // Promise is available, we will use it:
+  /* istanbul ignore if */
+  if (typeof Promise !== 'undefined' && isNative(Promise)) {
+    var p = Promise.resolve();
+    var noop = function noop() {};
+    timerFunc = function () {
+      p.then(nextTickHandler);
+      // in problematic UIWebViews, Promise.then doesn't completely break, but
+      // it can get stuck in a weird state where callbacks are pushed into the
+      // microtask queue but the queue isn't being flushed, until the browser
+      // needs to do some other work, e.g. handle a timer. Therefore we can
+      // "force" the microtask queue to be flushed by adding an empty timer.
+      if (isIOS) setTimeout(noop);
+    };
+  } else if (typeof MutationObserver !== 'undefined') {
+    // use MutationObserver where native Promise is not available,
+    // e.g. IE11, iOS7, Android 4.4
+    var counter = 1;
+    var observer = new MutationObserver(nextTickHandler);
+    var textNode = document.createTextNode(String(counter));
+    observer.observe(textNode, {
+      characterData: true
+    });
+    timerFunc = function () {
+      counter = (counter + 1) % 2;
+      textNode.data = String(counter);
+    };
+  } else {
+    // fallback to setTimeout
+    /* istanbul ignore next */
+    timerFunc = setTimeout;
+  }
+
+  return function (cb, ctx) {
+    var func = ctx ? function () {
+      cb.call(ctx);
+    } : cb;
+    callbacks.push(func);
+    if (pending) return;
+    pending = true;
+    timerFunc(nextTickHandler, 0);
+  };
+})();
+
+var _Set = undefined;
+/* istanbul ignore if */
+if (typeof Set !== 'undefined' && isNative(Set)) {
+  // use native Set when available.
+  _Set = Set;
+} else {
+  // a non-standard Set polyfill that only works with primitive keys.
+  _Set = function () {
+    this.set = Object.create(null);
+  };
+  _Set.prototype.has = function (key) {
+    return this.set[key] !== undefined;
+  };
+  _Set.prototype.add = function (key) {
+    this.set[key] = 1;
+  };
+  _Set.prototype.clear = function () {
+    this.set = Object.create(null);
+  };
+}
+
+function Cache(limit) {
+  this.size = 0;
+  this.limit = limit;
+  this.head = this.tail = undefined;
+  this._keymap = Object.create(null);
+}
+
+var p = Cache.prototype;
+
+/**
+ * Put <value> into the cache associated with <key>.
+ * Returns the entry which was removed to make room for
+ * the new entry. Otherwise undefined is returned.
+ * (i.e. if there was enough room already).
+ *
+ * @param {String} key
+ * @param {*} value
+ * @return {Entry|undefined}
+ */
+
+p.put = function (key, value) {
+  var removed;
+
+  var entry = this.get(key, true);
+  if (!entry) {
+    if (this.size === this.limit) {
+      removed = this.shift();
+    }
+    entry = {
+      key: key
+    };
+    this._keymap[key] = entry;
+    if (this.tail) {
+      this.tail.newer = entry;
+      entry.older = this.tail;
+    } else {
+      this.head = entry;
+    }
+    this.tail = entry;
+    this.size++;
+  }
+  entry.value = value;
+
+  return removed;
+};
+
+/**
+ * Purge the least recently used (oldest) entry from the
+ * cache. Returns the removed entry or undefined if the
+ * cache was empty.
+ */
+
+p.shift = function () {
+  var entry = this.head;
+  if (entry) {
+    this.head = this.head.newer;
+    this.head.older = undefined;
+    entry.newer = entry.older = undefined;
+    this._keymap[entry.key] = undefined;
+    this.size--;
+  }
+  return entry;
+};
+
+/**
+ * Get and register recent use of <key>. Returns the value
+ * associated with <key> or undefined if not in cache.
+ *
+ * @param {String} key
+ * @param {Boolean} returnEntry
+ * @return {Entry|*}
+ */
+
+p.get = function (key, returnEntry) {
+  var entry = this._keymap[key];
+  if (entry === undefined) return;
+  if (entry === this.tail) {
+    return returnEntry ? entry : entry.value;
+  }
+  // HEAD--------------TAIL
+  //   <.older   .newer>
+  //  <--- add direction --
+  //   A  B  C  <D>  E
+  if (entry.newer) {
+    if (entry === this.head) {
+      this.head = entry.newer;
+    }
+    entry.newer.older = entry.older; // C <-- E.
+  }
+  if (entry.older) {
+    entry.older.newer = entry.newer; // C. --> E
+  }
+  entry.newer = undefined; // D --x
+  entry.older = this.tail; // D. --> E
+  if (this.tail) {
+    this.tail.newer = entry; // E. <-- D
+  }
+  this.tail = entry;
+  return returnEntry ? entry : entry.value;
+};
+
+var cache$1 = new Cache(1000);
+var reservedArgRE = /^in$|^-?\d+/;
+
+/**
+ * Parser state
+ */
+
+var str;
+var dir;
+var len;
+var index;
+var chr;
+var state;
+var startState = 0;
+var filterState = 1;
+var filterNameState = 2;
+var filterArgState = 3;
+
+var doubleChr = 0x22;
+var singleChr = 0x27;
+var pipeChr = 0x7C;
+var escapeChr = 0x5C;
+var spaceChr = 0x20;
+
+var expStartChr = { 0x5B: 1, 0x7B: 1, 0x28: 1 };
+var expChrPair = { 0x5B: 0x5D, 0x7B: 0x7D, 0x28: 0x29 };
+
+function peek() {
+  return str.charCodeAt(index + 1);
+}
+
+function next() {
+  return str.charCodeAt(++index);
+}
+
+function eof() {
+  return index >= len;
+}
+
+function eatSpace() {
+  while (peek() === spaceChr) {
+    next();
+  }
+}
+
+function isStringStart(chr) {
+  return chr === doubleChr || chr === singleChr;
+}
+
+function isExpStart(chr) {
+  return expStartChr[chr];
+}
+
+function isExpEnd(start, chr) {
+  return expChrPair[start] === chr;
+}
+
+function parseString() {
+  var stringQuote = next();
+  var chr;
+  while (!eof()) {
+    chr = next();
+    // escape char
+    if (chr === escapeChr) {
+      next();
+    } else if (chr === stringQuote) {
+      break;
+    }
+  }
+}
+
+function parseSpecialExp(chr) {
+  var inExp = 0;
+  var startChr = chr;
+
+  while (!eof()) {
+    chr = peek();
+    if (isStringStart(chr)) {
+      parseString();
+      continue;
+    }
+
+    if (startChr === chr) {
+      inExp++;
+    }
+    if (isExpEnd(startChr, chr)) {
+      inExp--;
+    }
+
+    next();
+
+    if (inExp === 0) {
+      break;
+    }
+  }
+}
+
+/**
+ * syntax:
+ * expression | filterName  [arg  arg [| filterName arg arg]]
+ */
+
+function parseExpression() {
+  var start = index;
+  while (!eof()) {
+    chr = peek();
+    if (isStringStart(chr)) {
+      parseString();
+    } else if (isExpStart(chr)) {
+      parseSpecialExp(chr);
+    } else if (chr === pipeChr) {
+      next();
+      chr = peek();
+      if (chr === pipeChr) {
+        next();
+      } else {
+        if (state === startState || state === filterArgState) {
+          state = filterState;
+        }
+        break;
+      }
+    } else if (chr === spaceChr && (state === filterNameState || state === filterArgState)) {
+      eatSpace();
+      break;
+    } else {
+      if (state === filterState) {
+        state = filterNameState;
+      }
+      next();
+    }
+  }
+
+  return str.slice(start + 1, index) || null;
+}
+
+function parseFilterList() {
+  var filters = [];
+  while (!eof()) {
+    filters.push(parseFilter());
+  }
+  return filters;
+}
+
+function parseFilter() {
+  var filter = {};
+  var args;
+
+  state = filterState;
+  filter.name = parseExpression().trim();
+
+  state = filterArgState;
+  args = parseFilterArguments();
+
+  if (args.length) {
+    filter.args = args;
+  }
+  return filter;
+}
+
+function parseFilterArguments() {
+  var args = [];
+  while (!eof() && state !== filterState) {
+    var arg = parseExpression();
+    if (!arg) {
+      break;
+    }
+    args.push(processFilterArg(arg));
+  }
+
+  return args;
+}
+
+/**
+ * Check if an argument is dynamic and strip quotes.
+ *
+ * @param {String} arg
+ * @return {Object}
+ */
+
+function processFilterArg(arg) {
+  if (reservedArgRE.test(arg)) {
+    return {
+      value: toNumber(arg),
+      dynamic: false
+    };
+  } else {
+    var stripped = stripQuotes(arg);
+    var dynamic = stripped === arg;
+    return {
+      value: dynamic ? arg : stripped,
+      dynamic: dynamic
+    };
+  }
+}
+
+/**
+ * Parse a directive value and extract the expression
+ * and its filters into a descriptor.
+ *
+ * Example:
+ *
+ * "a + 1 | uppercase" will yield:
+ * {
+ *   expression: 'a + 1',
+ *   filters: [
+ *     { name: 'uppercase', args: null }
+ *   ]
+ * }
+ *
+ * @param {String} s
+ * @return {Object}
+ */
+
+function parseDirective(s) {
+  var hit = cache$1.get(s);
+  if (hit) {
+    return hit;
+  }
+
+  // reset parser state
+  str = s;
+  dir = {};
+  len = str.length;
+  index = -1;
+  chr = '';
+  state = startState;
+
+  var filters;
+
+  if (str.indexOf('|') < 0) {
+    dir.expression = str.trim();
+  } else {
+    dir.expression = parseExpression().trim();
+    filters = parseFilterList();
+    if (filters.length) {
+      dir.filters = filters;
+    }
+  }
+
+  cache$1.put(s, dir);
+  return dir;
+}
+
+var directive = Object.freeze({
+  parseDirective: parseDirective
+});
+
+var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g;
+var cache = undefined;
+var tagRE = undefined;
+var htmlRE = undefined;
+/**
+ * Escape a string so it can be used in a RegExp
+ * constructor.
+ *
+ * @param {String} str
+ */
+
+function escapeRegex(str) {
+  return str.replace(regexEscapeRE, '\\$&');
+}
+
+function compileRegex() {
+  var open = escapeRegex(config.delimiters[0]);
+  var close = escapeRegex(config.delimiters[1]);
+  var unsafeOpen = escapeRegex(config.unsafeDelimiters[0]);
+  var unsafeClose = escapeRegex(config.unsafeDelimiters[1]);
+  tagRE = new RegExp(unsafeOpen + '((?:.|\\n)+?)' + unsafeClose + '|' + open + '((?:.|\\n)+?)' + close, 'g');
+  htmlRE = new RegExp('^' + unsafeOpen + '((?:.|\\n)+?)' + unsafeClose + '$');
+  // reset cache
+  cache = new Cache(1000);
+}
+
+/**
+ * Parse a template text string into an array of tokens.
+ *
+ * @param {String} text
+ * @return {Array<Object> | null}
+ *               - {String} type
+ *               - {String} value
+ *               - {Boolean} [html]
+ *               - {Boolean} [oneTime]
+ */
+
+function parseText(text) {
+  if (!cache) {
+    compileRegex();
+  }
+  var hit = cache.get(text);
+  if (hit) {
+    return hit;
+  }
+  if (!tagRE.test(text)) {
+    return null;
+  }
+  var tokens = [];
+  var lastIndex = tagRE.lastIndex = 0;
+  var match, index, html, value, first, oneTime;
+  /* eslint-disable no-cond-assign */
+  while (match = tagRE.exec(text)) {
+    /* eslint-enable no-cond-assign */
+    index = match.index;
+    // push text token
+    if (index > lastIndex) {
+      tokens.push({
+        value: text.slice(lastIndex, index)
+      });
+    }
+    // tag token
+    html = htmlRE.test(match[0]);
+    value = html ? match[1] : match[2];
+    first = value.charCodeAt(0);
+    oneTime = first === 42; // *
+    value = oneTime ? value.slice(1) : value;
+    tokens.push({
+      tag: true,
+      value: value.trim(),
+      html: html,
+      oneTime: oneTime
+    });
+    lastIndex = index + match[0].length;
+  }
+  if (lastIndex < text.length) {
+    tokens.push({
+      value: text.slice(lastIndex)
+    });
+  }
+  cache.put(text, tokens);
+  return tokens;
+}
+
+/**
+ * Format a list of tokens into an expression.
+ * e.g. tokens parsed from 'a {{b}} c' can be serialized
+ * into one single expression as '"a " + b + " c"'.
+ *
+ * @param {Array} tokens
+ * @param {Vue} [vm]
+ * @return {String}
+ */
+
+function tokensToExp(tokens, vm) {
+  if (tokens.length > 1) {
+    return tokens.map(function (token) {
+      return formatToken(token, vm);
+    }).join('+');
+  } else {
+    return formatToken(tokens[0], vm, true);
+  }
+}
+
+/**
+ * Format a single token.
+ *
+ * @param {Object} token
+ * @param {Vue} [vm]
+ * @param {Boolean} [single]
+ * @return {String}
+ */
+
+function formatToken(token, vm, single) {
+  return token.tag ? token.oneTime && vm ? '"' + vm.$eval(token.value) + '"' : inlineFilters(token.value, single) : '"' + token.value + '"';
+}
+
+/**
+ * For an attribute with multiple interpolation tags,
+ * e.g. attr="some-{{thing | filter}}", in order to combine
+ * the whole thing into a single watchable expression, we
+ * have to inline those filters. This function does exactly
+ * that. This is a bit hacky but it avoids heavy changes
+ * to directive parser and watcher mechanism.
+ *
+ * @param {String} exp
+ * @param {Boolean} single
+ * @return {String}
+ */
+
+var filterRE = /[^|]\|[^|]/;
+function inlineFilters(exp, single) {
+  if (!filterRE.test(exp)) {
+    return single ? exp : '(' + exp + ')';
+  } else {
+    var dir = parseDirective(exp);
+    if (!dir.filters) {
+      return '(' + exp + ')';
+    } else {
+      return 'this._applyFilters(' + dir.expression + // value
+      ',null,' + // oldValue (null for read)
+      JSON.stringify(dir.filters) + // filter descriptors
+      ',false)'; // write?
+    }
+  }
+}
+
+var text = Object.freeze({
+  compileRegex: compileRegex,
+  parseText: parseText,
+  tokensToExp: tokensToExp
+});
+
+var delimiters = ['{{', '}}'];
+var unsafeDelimiters = ['{{{', '}}}'];
+
+var config = Object.defineProperties({
+
+  /**
+   * Whether to print debug messages.
+   * Also enables stack trace for warnings.
+   *
+   * @type {Boolean}
+   */
+
+  debug: false,
+
+  /**
+   * Whether to suppress warnings.
+   *
+   * @type {Boolean}
+   */
+
+  silent: false,
+
+  /**
+   * Whether to use async rendering.
+   */
+
+  async: true,
+
+  /**
+   * Whether to warn against errors caught when evaluating
+   * expressions.
+   */
+
+  warnExpressionErrors: true,
+
+  /**
+   * Whether to allow devtools inspection.
+   * Disabled by default in production builds.
+   */
+
+  devtools: 'development' !== 'production',
+
+  /**
+   * Internal flag to indicate the delimiters have been
+   * changed.
+   *
+   * @type {Boolean}
+   */
+
+  _delimitersChanged: true,
+
+  /**
+   * List of asset types that a component can own.
+   *
+   * @type {Array}
+   */
+
+  _assetTypes: ['component', 'directive', 'elementDirective', 'filter', 'transition', 'partial'],
+
+  /**
+   * prop binding modes
+   */
+
+  _propBindingModes: {
+    ONE_WAY: 0,
+    TWO_WAY: 1,
+    ONE_TIME: 2
+  },
+
+  /**
+   * Max circular updates allowed in a batcher flush cycle.
+   */
+
+  _maxUpdateCount: 100
+
+}, {
+  delimiters: { /**
+                 * Interpolation delimiters. Changing these would trigger
+                 * the text parser to re-compile the regular expressions.
+                 *
+                 * @type {Array<String>}
+                 */
+
+    get: function get() {
+      return delimiters;
+    },
+    set: function set(val) {
+      delimiters = val;
+      compileRegex();
+    },
+    configurable: true,
+    enumerable: true
+  },
+  unsafeDelimiters: {
+    get: function get() {
+      return unsafeDelimiters;
+    },
+    set: function set(val) {
+      unsafeDelimiters = val;
+      compileRegex();
+    },
+    configurable: true,
+    enumerable: true
+  }
+});
+
+var warn = undefined;
+var formatComponentName = undefined;
+
+if ('development' !== 'production') {
+  (function () {
+    var hasConsole = typeof console !== 'undefined';
+
+    warn = function (msg, vm) {
+      if (hasConsole && !config.silent) {
+        console.error('[Vue warn]: ' + msg + (vm ? formatComponentName(vm) : ''));
+      }
+    };
+
+    formatComponentName = function (vm) {
+      var name = vm._isVue ? vm.$options.name : vm.name;
+      return name ? ' (found in component: <' + hyphenate(name) + '>)' : '';
+    };
+  })();
+}
+
+/**
+ * Append with transition.
+ *
+ * @param {Element} el
+ * @param {Element} target
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+function appendWithTransition(el, target, vm, cb) {
+  applyTransition(el, 1, function () {
+    target.appendChild(el);
+  }, vm, cb);
+}
+
+/**
+ * InsertBefore with transition.
+ *
+ * @param {Element} el
+ * @param {Element} target
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+function beforeWithTransition(el, target, vm, cb) {
+  applyTransition(el, 1, function () {
+    before(el, target);
+  }, vm, cb);
+}
+
+/**
+ * Remove with transition.
+ *
+ * @param {Element} el
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+function removeWithTransition(el, vm, cb) {
+  applyTransition(el, -1, function () {
+    remove(el);
+  }, vm, cb);
+}
+
+/**
+ * Apply transitions with an operation callback.
+ *
+ * @param {Element} el
+ * @param {Number} direction
+ *                  1: enter
+ *                 -1: leave
+ * @param {Function} op - the actual DOM operation
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+function applyTransition(el, direction, op, vm, cb) {
+  var transition = el.__v_trans;
+  if (!transition ||
+  // skip if there are no js hooks and CSS transition is
+  // not supported
+  !transition.hooks && !transitionEndEvent ||
+  // skip transitions for initial compile
+  !vm._isCompiled ||
+  // if the vm is being manipulated by a parent directive
+  // during the parent's compilation phase, skip the
+  // animation.
+  vm.$parent && !vm.$parent._isCompiled) {
+    op();
+    if (cb) cb();
+    return;
+  }
+  var action = direction > 0 ? 'enter' : 'leave';
+  transition[action](op, cb);
+}
+
+var transition = Object.freeze({
+  appendWithTransition: appendWithTransition,
+  beforeWithTransition: beforeWithTransition,
+  removeWithTransition: removeWithTransition,
+  applyTransition: applyTransition
+});
+
+/**
+ * Query an element selector if it's not an element already.
+ *
+ * @param {String|Element} el
+ * @return {Element}
+ */
+
+function query(el) {
+  if (typeof el === 'string') {
+    var selector = el;
+    el = document.querySelector(el);
+    if (!el) {
+      'development' !== 'production' && warn('Cannot find element: ' + selector);
+    }
+  }
+  return el;
+}
+
+/**
+ * Check if a node is in the document.
+ * Note: document.documentElement.contains should work here
+ * but always returns false for comment nodes in phantomjs,
+ * making unit tests difficult. This is fixed by doing the
+ * contains() check on the node's parentNode instead of
+ * the node itself.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+function inDoc(node) {
+  if (!node) return false;
+  var doc = node.ownerDocument.documentElement;
+  var parent = node.parentNode;
+  return doc === node || doc === parent || !!(parent && parent.nodeType === 1 && doc.contains(parent));
+}
+
+/**
+ * Get and remove an attribute from a node.
+ *
+ * @param {Node} node
+ * @param {String} _attr
+ */
+
+function getAttr(node, _attr) {
+  var val = node.getAttribute(_attr);
+  if (val !== null) {
+    node.removeAttribute(_attr);
+  }
+  return val;
+}
+
+/**
+ * Get an attribute with colon or v-bind: prefix.
+ *
+ * @param {Node} node
+ * @param {String} name
+ * @return {String|null}
+ */
+
+function getBindAttr(node, name) {
+  var val = getAttr(node, ':' + name);
+  if (val === null) {
+    val = getAttr(node, 'v-bind:' + name);
+  }
+  return val;
+}
+
+/**
+ * Check the presence of a bind attribute.
+ *
+ * @param {Node} node
+ * @param {String} name
+ * @return {Boolean}
+ */
+
+function hasBindAttr(node, name) {
+  return node.hasAttribute(name) || node.hasAttribute(':' + name) || node.hasAttribute('v-bind:' + name);
+}
+
+/**
+ * Insert el before target
+ *
+ * @param {Element} el
+ * @param {Element} target
+ */
+
+function before(el, target) {
+  target.parentNode.insertBefore(el, target);
+}
+
+/**
+ * Insert el after target
+ *
+ * @param {Element} el
+ * @param {Element} target
+ */
+
+function after(el, target) {
+  if (target.nextSibling) {
+    before(el, target.nextSibling);
+  } else {
+    target.parentNode.appendChild(el);
+  }
+}
+
+/**
+ * Remove el from DOM
+ *
+ * @param {Element} el
+ */
+
+function remove(el) {
+  el.parentNode.removeChild(el);
+}
+
+/**
+ * Prepend el to target
+ *
+ * @param {Element} el
+ * @param {Element} target
+ */
+
+function prepend(el, target) {
+  if (target.firstChild) {
+    before(el, target.firstChild);
+  } else {
+    target.appendChild(el);
+  }
+}
+
+/**
+ * Replace target with el
+ *
+ * @param {Element} target
+ * @param {Element} el
+ */
+
+function replace(target, el) {
+  var parent = target.parentNode;
+  if (parent) {
+    parent.replaceChild(el, target);
+  }
+}
+
+/**
+ * Add event listener shorthand.
+ *
+ * @param {Element} el
+ * @param {String} event
+ * @param {Function} cb
+ * @param {Boolean} [useCapture]
+ */
+
+function on(el, event, cb, useCapture) {
+  el.addEventListener(event, cb, useCapture);
+}
+
+/**
+ * Remove event listener shorthand.
+ *
+ * @param {Element} el
+ * @param {String} event
+ * @param {Function} cb
+ */
+
+function off(el, event, cb) {
+  el.removeEventListener(event, cb);
+}
+
+/**
+ * For IE9 compat: when both class and :class are present
+ * getAttribute('class') returns wrong value...
+ *
+ * @param {Element} el
+ * @return {String}
+ */
+
+function getClass(el) {
+  var classname = el.className;
+  if (typeof classname === 'object') {
+    classname = classname.baseVal || '';
+  }
+  return classname;
+}
+
+/**
+ * In IE9, setAttribute('class') will result in empty class
+ * if the element also has the :class attribute; However in
+ * PhantomJS, setting `className` does not work on SVG elements...
+ * So we have to do a conditional check here.
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+function setClass(el, cls) {
+  /* istanbul ignore if */
+  if (isIE9 && !/svg$/.test(el.namespaceURI)) {
+    el.className = cls;
+  } else {
+    el.setAttribute('class', cls);
+  }
+}
+
+/**
+ * Add class with compatibility for IE & SVG
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+function addClass(el, cls) {
+  if (el.classList) {
+    el.classList.add(cls);
+  } else {
+    var cur = ' ' + getClass(el) + ' ';
+    if (cur.indexOf(' ' + cls + ' ') < 0) {
+      setClass(el, (cur + cls).trim());
+    }
+  }
+}
+
+/**
+ * Remove class with compatibility for IE & SVG
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+function removeClass(el, cls) {
+  if (el.classList) {
+    el.classList.remove(cls);
+  } else {
+    var cur = ' ' + getClass(el) + ' ';
+    var tar = ' ' + cls + ' ';
+    while (cur.indexOf(tar) >= 0) {
+      cur = cur.replace(tar, ' ');
+    }
+    setClass(el, cur.trim());
+  }
+  if (!el.className) {
+    el.removeAttribute('class');
+  }
+}
+
+/**
+ * Extract raw content inside an element into a temporary
+ * container div
+ *
+ * @param {Element} el
+ * @param {Boolean} asFragment
+ * @return {Element|DocumentFragment}
+ */
+
+function extractContent(el, asFragment) {
+  var child;
+  var rawContent;
+  /* istanbul ignore if */
+  if (isTemplate(el) && isFragment(el.content)) {
+    el = el.content;
+  }
+  if (el.hasChildNodes()) {
+    trimNode(el);
+    rawContent = asFragment ? document.createDocumentFragment() : document.createElement('div');
+    /* eslint-disable no-cond-assign */
+    while (child = el.firstChild) {
+      /* eslint-enable no-cond-assign */
+      rawContent.appendChild(child);
+    }
+  }
+  return rawContent;
+}
+
+/**
+ * Trim possible empty head/tail text and comment
+ * nodes inside a parent.
+ *
+ * @param {Node} node
+ */
+
+function trimNode(node) {
+  var child;
+  /* eslint-disable no-sequences */
+  while ((child = node.firstChild, isTrimmable(child))) {
+    node.removeChild(child);
+  }
+  while ((child = node.lastChild, isTrimmable(child))) {
+    node.removeChild(child);
+  }
+  /* eslint-enable no-sequences */
+}
+
+function isTrimmable(node) {
+  return node && (node.nodeType === 3 && !node.data.trim() || node.nodeType === 8);
+}
+
+/**
+ * Check if an element is a template tag.
+ * Note if the template appears inside an SVG its tagName
+ * will be in lowercase.
+ *
+ * @param {Element} el
+ */
+
+function isTemplate(el) {
+  return el.tagName && el.tagName.toLowerCase() === 'template';
+}
+
+/**
+ * Create an "anchor" for performing dom insertion/removals.
+ * This is used in a number of scenarios:
+ * - fragment instance
+ * - v-html
+ * - v-if
+ * - v-for
+ * - component
+ *
+ * @param {String} content
+ * @param {Boolean} persist - IE trashes empty textNodes on
+ *                            cloneNode(true), so in certain
+ *                            cases the anchor needs to be
+ *                            non-empty to be persisted in
+ *                            templates.
+ * @return {Comment|Text}
+ */
+
+function createAnchor(content, persist) {
+  var anchor = config.debug ? document.createComment(content) : document.createTextNode(persist ? ' ' : '');
+  anchor.__v_anchor = true;
+  return anchor;
+}
+
+/**
+ * Find a component ref attribute that starts with $.
+ *
+ * @param {Element} node
+ * @return {String|undefined}
+ */
+
+var refRE = /^v-ref:/;
+
+function findRef(node) {
+  if (node.hasAttributes()) {
+    var attrs = node.attributes;
+    for (var i = 0, l = attrs.length; i < l; i++) {
+      var name = attrs[i].name;
+      if (refRE.test(name)) {
+        return camelize(name.replace(refRE, ''));
+      }
+    }
+  }
+}
+
+/**
+ * Map a function to a range of nodes .
+ *
+ * @param {Node} node
+ * @param {Node} end
+ * @param {Function} op
+ */
+
+function mapNodeRange(node, end, op) {
+  var next;
+  while (node !== end) {
+    next = node.nextSibling;
+    op(node);
+    node = next;
+  }
+  op(end);
+}
+
+/**
+ * Remove a range of nodes with transition, store
+ * the nodes in a fragment with correct ordering,
+ * and call callback when done.
+ *
+ * @param {Node} start
+ * @param {Node} end
+ * @param {Vue} vm
+ * @param {DocumentFragment} frag
+ * @param {Function} cb
+ */
+
+function removeNodeRange(start, end, vm, frag, cb) {
+  var done = false;
+  var removed = 0;
+  var nodes = [];
+  mapNodeRange(start, end, function (node) {
+    if (node === end) done = true;
+    nodes.push(node);
+    removeWithTransition(node, vm, onRemoved);
+  });
+  function onRemoved() {
+    removed++;
+    if (done && removed >= nodes.length) {
+      for (var i = 0; i < nodes.length; i++) {
+        frag.appendChild(nodes[i]);
+      }
+      cb && cb();
+    }
+  }
+}
+
+/**
+ * Check if a node is a DocumentFragment.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+function isFragment(node) {
+  return node && node.nodeType === 11;
+}
+
+/**
+ * Get outerHTML of elements, taking care
+ * of SVG elements in IE as well.
+ *
+ * @param {Element} el
+ * @return {String}
+ */
+
+function getOuterHTML(el) {
+  if (el.outerHTML) {
+    return el.outerHTML;
+  } else {
+    var container = document.createElement('div');
+    container.appendChild(el.cloneNode(true));
+    return container.innerHTML;
+  }
+}
+
+var commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/i;
+var reservedTagRE = /^(slot|partial|component)$/i;
+
+var isUnknownElement = undefined;
+if ('development' !== 'production') {
+  isUnknownElement = function (el, tag) {
+    if (tag.indexOf('-') > -1) {
+      // http://stackoverflow.com/a/28210364/1070244
+      return el.constructor === window.HTMLUnknownElement || el.constructor === window.HTMLElement;
+    } else {
+      return (/HTMLUnknownElement/.test(el.toString()) &&
+        // Chrome returns unknown for several HTML5 elements.
+        // https://code.google.com/p/chromium/issues/detail?id=540526
+        // Firefox returns unknown for some "Interactive elements."
+        !/^(data|time|rtc|rb|details|dialog|summary)$/.test(tag)
+      );
+    }
+  };
+}
+
+/**
+ * Check if an element is a component, if yes return its
+ * component id.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Object|undefined}
+ */
+
+function checkComponentAttr(el, options) {
+  var tag = el.tagName.toLowerCase();
+  var hasAttrs = el.hasAttributes();
+  if (!commonTagRE.test(tag) && !reservedTagRE.test(tag)) {
+    if (resolveAsset(options, 'components', tag)) {
+      return { id: tag };
+    } else {
+      var is = hasAttrs && getIsBinding(el, options);
+      if (is) {
+        return is;
+      } else if ('development' !== 'production') {
+        var expectedTag = options._componentNameMap && options._componentNameMap[tag];
+        if (expectedTag) {
+          warn('Unknown custom element: <' + tag + '> - ' + 'did you mean <' + expectedTag + '>? ' + 'HTML is case-insensitive, remember to use kebab-case in templates.');
+        } else if (isUnknownElement(el, tag)) {
+          warn('Unknown custom element: <' + tag + '> - did you ' + 'register the component correctly? For recursive components, ' + 'make sure to provide the "name" option.');
+        }
+      }
+    }
+  } else if (hasAttrs) {
+    return getIsBinding(el, options);
+  }
+}
+
+/**
+ * Get "is" binding from an element.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Object|undefined}
+ */
+
+function getIsBinding(el, options) {
+  // dynamic syntax
+  var exp = el.getAttribute('is');
+  if (exp != null) {
+    if (resolveAsset(options, 'components', exp)) {
+      el.removeAttribute('is');
+      return { id: exp };
+    }
+  } else {
+    exp = getBindAttr(el, 'is');
+    if (exp != null) {
+      return { id: exp, dynamic: true };
+    }
+  }
+}
+
+/**
+ * Option overwriting strategies are functions that handle
+ * how to merge a parent option value and a child option
+ * value into the final value.
+ *
+ * All strategy functions follow the same signature:
+ *
+ * @param {*} parentVal
+ * @param {*} childVal
+ * @param {Vue} [vm]
+ */
+
+var strats = config.optionMergeStrategies = Object.create(null);
+
+/**
+ * Helper that recursively merges two data objects together.
+ */
+
+function mergeData(to, from) {
+  var key, toVal, fromVal;
+  for (key in from) {
+    toVal = to[key];
+    fromVal = from[key];
+    if (!hasOwn(to, key)) {
+      set(to, key, fromVal);
+    } else if (isObject(toVal) && isObject(fromVal)) {
+      mergeData(toVal, fromVal);
+    }
+  }
+  return to;
+}
+
+/**
+ * Data
+ */
+
+strats.data = function (parentVal, childVal, vm) {
+  if (!vm) {
+    // in a Vue.extend merge, both should be functions
+    if (!childVal) {
+      return parentVal;
+    }
+    if (typeof childVal !== 'function') {
+      'development' !== 'production' && warn('The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm);
+      return parentVal;
+    }
+    if (!parentVal) {
+      return childVal;
+    }
+    // when parentVal & childVal are both present,
+    // we need to return a function that returns the
+    // merged result of both functions... no need to
+    // check if parentVal is a function here because
+    // it has to be a function to pass previous merges.
+    return function mergedDataFn() {
+      return mergeData(childVal.call(this), parentVal.call(this));
+    };
+  } else if (parentVal || childVal) {
+    return function mergedInstanceDataFn() {
+      // instance merge
+      var instanceData = typeof childVal === 'function' ? childVal.call(vm) : childVal;
+      var defaultData = typeof parentVal === 'function' ? parentVal.call(vm) : undefined;
+      if (instanceData) {
+        return mergeData(instanceData, defaultData);
+      } else {
+        return defaultData;
+      }
+    };
+  }
+};
+
+/**
+ * El
+ */
+
+strats.el = function (parentVal, childVal, vm) {
+  if (!vm && childVal && typeof childVal !== 'function') {
+    'development' !== 'production' && warn('The "el" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm);
+    return;
+  }
+  var ret = childVal || parentVal;
+  // invoke the element factory if this is instance merge
+  return vm && typeof ret === 'function' ? ret.call(vm) : ret;
+};
+
+/**
+ * Hooks and param attributes are merged as arrays.
+ */
+
+strats.init = strats.created = strats.ready = strats.attached = strats.detached = strats.beforeCompile = strats.compiled = strats.beforeDestroy = strats.destroyed = strats.activate = function (parentVal, childVal) {
+  return childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [childVal] : parentVal;
+};
+
+/**
+ * Assets
+ *
+ * When a vm is present (instance creation), we need to do
+ * a three-way merge between constructor options, instance
+ * options and parent options.
+ */
+
+function mergeAssets(parentVal, childVal) {
+  var res = Object.create(parentVal || null);
+  return childVal ? extend(res, guardArrayAssets(childVal)) : res;
+}
+
+config._assetTypes.forEach(function (type) {
+  strats[type + 's'] = mergeAssets;
+});
+
+/**
+ * Events & Watchers.
+ *
+ * Events & watchers hashes should not overwrite one
+ * another, so we merge them as arrays.
+ */
+
+strats.watch = strats.events = function (parentVal, childVal) {
+  if (!childVal) return parentVal;
+  if (!parentVal) return childVal;
+  var ret = {};
+  extend(ret, parentVal);
+  for (var key in childVal) {
+    var parent = ret[key];
+    var child = childVal[key];
+    if (parent && !isArray(parent)) {
+      parent = [parent];
+    }
+    ret[key] = parent ? parent.concat(child) : [child];
+  }
+  return ret;
+};
+
+/**
+ * Other object hashes.
+ */
+
+strats.props = strats.methods = strats.computed = function (parentVal, childVal) {
+  if (!childVal) return parentVal;
+  if (!parentVal) return childVal;
+  var ret = Object.create(null);
+  extend(ret, parentVal);
+  extend(ret, childVal);
+  return ret;
+};
+
+/**
+ * Default strategy.
+ */
+
+var defaultStrat = function defaultStrat(parentVal, childVal) {
+  return childVal === undefined ? parentVal : childVal;
+};
+
+/**
+ * Make sure component options get converted to actual
+ * constructors.
+ *
+ * @param {Object} options
+ */
+
+function guardComponents(options) {
+  if (options.components) {
+    var components = options.components = guardArrayAssets(options.components);
+    var ids = Object.keys(components);
+    var def;
+    if ('development' !== 'production') {
+      var map = options._componentNameMap = {};
+    }
+    for (var i = 0, l = ids.length; i < l; i++) {
+      var key = ids[i];
+      if (commonTagRE.test(key) || reservedTagRE.test(key)) {
+        'development' !== 'production' && warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + key);
+        continue;
+      }
+      // record a all lowercase <-> kebab-case mapping for
+      // possible custom element case error warning
+      if ('development' !== 'production') {
+        map[key.replace(/-/g, '').toLowerCase()] = hyphenate(key);
+      }
+      def = components[key];
+      if (isPlainObject(def)) {
+        components[key] = Vue.extend(def);
+      }
+    }
+  }
+}
+
+/**
+ * Ensure all props option syntax are normalized into the
+ * Object-based format.
+ *
+ * @param {Object} options
+ */
+
+function guardProps(options) {
+  var props = options.props;
+  var i, val;
+  if (isArray(props)) {
+    options.props = {};
+    i = props.length;
+    while (i--) {
+      val = props[i];
+      if (typeof val === 'string') {
+        options.props[val] = null;
+      } else if (val.name) {
+        options.props[val.name] = val;
+      }
+    }
+  } else if (isPlainObject(props)) {
+    var keys = Object.keys(props);
+    i = keys.length;
+    while (i--) {
+      val = props[keys[i]];
+      if (typeof val === 'function') {
+        props[keys[i]] = { type: val };
+      }
+    }
+  }
+}
+
+/**
+ * Guard an Array-format assets option and converted it
+ * into the key-value Object format.
+ *
+ * @param {Object|Array} assets
+ * @return {Object}
+ */
+
+function guardArrayAssets(assets) {
+  if (isArray(assets)) {
+    var res = {};
+    var i = assets.length;
+    var asset;
+    while (i--) {
+      asset = assets[i];
+      var id = typeof asset === 'function' ? asset.options && asset.options.name || asset.id : asset.name || asset.id;
+      if (!id) {
+        'development' !== 'production' && warn('Array-syntax assets must provide a "name" or "id" field.');
+      } else {
+        res[id] = asset;
+      }
+    }
+    return res;
+  }
+  return assets;
+}
+
+/**
+ * Merge two option objects into a new one.
+ * Core utility used in both instantiation and inheritance.
+ *
+ * @param {Object} parent
+ * @param {Object} child
+ * @param {Vue} [vm] - if vm is present, indicates this is
+ *                     an instantiation merge.
+ */
+
+function mergeOptions(parent, child, vm) {
+  guardComponents(child);
+  guardProps(child);
+  if ('development' !== 'production') {
+    if (child.propsData && !vm) {
+      warn('propsData can only be used as an instantiation option.');
+    }
+  }
+  var options = {};
+  var key;
+  if (child['extends']) {
+    parent = typeof child['extends'] === 'function' ? mergeOptions(parent, child['extends'].options, vm) : mergeOptions(parent, child['extends'], vm);
+  }
+  if (child.mixins) {
+    for (var i = 0, l = child.mixins.length; i < l; i++) {
+      var mixin = child.mixins[i];
+      var mixinOptions = mixin.prototype instanceof Vue ? mixin.options : mixin;
+      parent = mergeOptions(parent, mixinOptions, vm);
+    }
+  }
+  for (key in parent) {
+    mergeField(key);
+  }
+  for (key in child) {
+    if (!hasOwn(parent, key)) {
+      mergeField(key);
+    }
+  }
+  function mergeField(key) {
+    var strat = strats[key] || defaultStrat;
+    options[key] = strat(parent[key], child[key], vm, key);
+  }
+  return options;
+}
+
+/**
+ * Resolve an asset.
+ * This function is used because child instances need access
+ * to assets defined in its ancestor chain.
+ *
+ * @param {Object} options
+ * @param {String} type
+ * @param {String} id
+ * @param {Boolean} warnMissing
+ * @return {Object|Function}
+ */
+
+function resolveAsset(options, type, id, warnMissing) {
+  /* istanbul ignore if */
+  if (typeof id !== 'string') {
+    return;
+  }
+  var assets = options[type];
+  var camelizedId;
+  var res = assets[id] ||
+  // camelCase ID
+  assets[camelizedId = camelize(id)] ||
+  // Pascal Case ID
+  assets[camelizedId.charAt(0).toUpperCase() + camelizedId.slice(1)];
+  if ('development' !== 'production' && warnMissing && !res) {
+    warn('Failed to resolve ' + type.slice(0, -1) + ': ' + id, options);
+  }
+  return res;
+}
+
+var uid$1 = 0;
+
+/**
+ * A dep is an observable that can have multiple
+ * directives subscribing to it.
+ *
+ * @constructor
+ */
+function Dep() {
+  this.id = uid$1++;
+  this.subs = [];
+}
+
+// the current target watcher being evaluated.
+// this is globally unique because there could be only one
+// watcher being evaluated at any time.
+Dep.target = null;
+
+/**
+ * Add a directive subscriber.
+ *
+ * @param {Directive} sub
+ */
+
+Dep.prototype.addSub = function (sub) {
+  this.subs.push(sub);
+};
+
+/**
+ * Remove a directive subscriber.
+ *
+ * @param {Directive} sub
+ */
+
+Dep.prototype.removeSub = function (sub) {
+  this.subs.$remove(sub);
+};
+
+/**
+ * Add self as a dependency to the target watcher.
+ */
+
+Dep.prototype.depend = function () {
+  Dep.target.addDep(this);
+};
+
+/**
+ * Notify all subscribers of a new value.
+ */
+
+Dep.prototype.notify = function () {
+  // stablize the subscriber list first
+  var subs = toArray(this.subs);
+  for (var i = 0, l = subs.length; i < l; i++) {
+    subs[i].update();
+  }
+};
+
+var arrayProto = Array.prototype;
+var arrayMethods = Object.create(arrayProto)
+
+/**
+ * Intercept mutating methods and emit events
+ */
+
+;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {
+  // cache original method
+  var original = arrayProto[method];
+  def(arrayMethods, method, function mutator() {
+    // avoid leaking arguments:
+    // http://jsperf.com/closure-with-arguments
+    var i = arguments.length;
+    var args = new Array(i);
+    while (i--) {
+      args[i] = arguments[i];
+    }
+    var result = original.apply(this, args);
+    var ob = this.__ob__;
+    var inserted;
+    switch (method) {
+      case 'push':
+        inserted = args;
+        break;
+      case 'unshift':
+        inserted = args;
+        break;
+      case 'splice':
+        inserted = args.slice(2);
+        break;
+    }
+    if (inserted) ob.observeArray(inserted);
+    // notify change
+    ob.dep.notify();
+    return result;
+  });
+});
+
+/**
+ * Swap the element at the given index with a new value
+ * and emits corresponding event.
+ *
+ * @param {Number} index
+ * @param {*} val
+ * @return {*} - replaced element
+ */
+
+def(arrayProto, '$set', function $set(index, val) {
+  if (index >= this.length) {
+    this.length = Number(index) + 1;
+  }
+  return this.splice(index, 1, val)[0];
+});
+
+/**
+ * Convenience method to remove the element at given index or target element reference.
+ *
+ * @param {*} item
+ */
+
+def(arrayProto, '$remove', function $remove(item) {
+  /* istanbul ignore if */
+  if (!this.length) return;
+  var index = indexOf(this, item);
+  if (index > -1) {
+    return this.splice(index, 1);
+  }
+});
+
+var arrayKeys = Object.getOwnPropertyNames(arrayMethods);
+
+/**
+ * By default, when a reactive property is set, the new value is
+ * also converted to become reactive. However in certain cases, e.g.
+ * v-for scope alias and props, we don't want to force conversion
+ * because the value may be a nested value under a frozen data structure.
+ *
+ * So whenever we want to set a reactive property without forcing
+ * conversion on the new value, we wrap that call inside this function.
+ */
+
+var shouldConvert = true;
+
+function withoutConversion(fn) {
+  shouldConvert = false;
+  fn();
+  shouldConvert = true;
+}
+
+/**
+ * Observer class that are attached to each observed
+ * object. Once attached, the observer converts target
+ * object's property keys into getter/setters that
+ * collect dependencies and dispatches updates.
+ *
+ * @param {Array|Object} value
+ * @constructor
+ */
+
+function Observer(value) {
+  this.value = value;
+  this.dep = new Dep();
+  def(value, '__ob__', this);
+  if (isArray(value)) {
+    var augment = hasProto ? protoAugment : copyAugment;
+    augment(value, arrayMethods, arrayKeys);
+    this.observeArray(value);
+  } else {
+    this.walk(value);
+  }
+}
+
+// Instance methods
+
+/**
+ * Walk through each property and convert them into
+ * getter/setters. This method should only be called when
+ * value type is Object.
+ *
+ * @param {Object} obj
+ */
+
+Observer.prototype.walk = function (obj) {
+  var keys = Object.keys(obj);
+  for (var i = 0, l = keys.length; i < l; i++) {
+    this.convert(keys[i], obj[keys[i]]);
+  }
+};
+
+/**
+ * Observe a list of Array items.
+ *
+ * @param {Array} items
+ */
+
+Observer.prototype.observeArray = function (items) {
+  for (var i = 0, l = items.length; i < l; i++) {
+    observe(items[i]);
+  }
+};
+
+/**
+ * Convert a property into getter/setter so we can emit
+ * the events when the property is accessed/changed.
+ *
+ * @param {String} key
+ * @param {*} val
+ */
+
+Observer.prototype.convert = function (key, val) {
+  defineReactive(this.value, key, val);
+};
+
+/**
+ * Add an owner vm, so that when $set/$delete mutations
+ * happen we can notify owner vms to proxy the keys and
+ * digest the watchers. This is only called when the object
+ * is observed as an instance's root $data.
+ *
+ * @param {Vue} vm
+ */
+
+Observer.prototype.addVm = function (vm) {
+  (this.vms || (this.vms = [])).push(vm);
+};
+
+/**
+ * Remove an owner vm. This is called when the object is
+ * swapped out as an instance's $data object.
+ *
+ * @param {Vue} vm
+ */
+
+Observer.prototype.removeVm = function (vm) {
+  this.vms.$remove(vm);
+};
+
+// helpers
+
+/**
+ * Augment an target Object or Array by intercepting
+ * the prototype chain using __proto__
+ *
+ * @param {Object|Array} target
+ * @param {Object} src
+ */
+
+function protoAugment(target, src) {
+  /* eslint-disable no-proto */
+  target.__proto__ = src;
+  /* eslint-enable no-proto */
+}
+
+/**
+ * Augment an target Object or Array by defining
+ * hidden properties.
+ *
+ * @param {Object|Array} target
+ * @param {Object} proto
+ */
+
+function copyAugment(target, src, keys) {
+  for (var i = 0, l = keys.length; i < l; i++) {
+    var key = keys[i];
+    def(target, key, src[key]);
+  }
+}
+
+/**
+ * Attempt to create an observer instance for a value,
+ * returns the new observer if successfully observed,
+ * or the existing observer if the value already has one.
+ *
+ * @param {*} value
+ * @param {Vue} [vm]
+ * @return {Observer|undefined}
+ * @static
+ */
+
+function observe(value, vm) {
+  if (!value || typeof value !== 'object') {
+    return;
+  }
+  var ob;
+  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
+    ob = value.__ob__;
+  } else if (shouldConvert && (isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue) {
+    ob = new Observer(value);
+  }
+  if (ob && vm) {
+    ob.addVm(vm);
+  }
+  return ob;
+}
+
+/**
+ * Define a reactive property on an Object.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {*} val
+ */
+
+function defineReactive(obj, key, val) {
+  var dep = new Dep();
+
+  var property = Object.getOwnPropertyDescriptor(obj, key);
+  if (property && property.configurable === false) {
+    return;
+  }
+
+  // cater for pre-defined getter/setters
+  var getter = property && property.get;
+  var setter = property && property.set;
+
+  var childOb = observe(val);
+  Object.defineProperty(obj, key, {
+    enumerable: true,
+    configurable: true,
+    get: function reactiveGetter() {
+      var value = getter ? getter.call(obj) : val;
+      if (Dep.target) {
+        dep.depend();
+        if (childOb) {
+          childOb.dep.depend();
+        }
+        if (isArray(value)) {
+          for (var e, i = 0, l = value.length; i < l; i++) {
+            e = value[i];
+            e && e.__ob__ && e.__ob__.dep.depend();
+          }
+        }
+      }
+      return value;
+    },
+    set: function reactiveSetter(newVal) {
+      var value = getter ? getter.call(obj) : val;
+      if (newVal === value) {
+        return;
+      }
+      if (setter) {
+        setter.call(obj, newVal);
+      } else {
+        val = newVal;
+      }
+      childOb = observe(newVal);
+      dep.notify();
+    }
+  });
+}
+
+
+
+var util = Object.freeze({
+	defineReactive: defineReactive,
+	set: set,
+	del: del,
+	hasOwn: hasOwn,
+	isLiteral: isLiteral,
+	isReserved: isReserved,
+	_toString: _toString,
+	toNumber: toNumber,
+	toBoolean: toBoolean,
+	stripQuotes: stripQuotes,
+	camelize: camelize,
+	hyphenate: hyphenate,
+	classify: classify,
+	bind: bind,
+	toArray: toArray,
+	extend: extend,
+	isObject: isObject,
+	isPlainObject: isPlainObject,
+	def: def,
+	debounce: _debounce,
+	indexOf: indexOf,
+	cancellable: cancellable,
+	looseEqual: looseEqual,
+	isArray: isArray,
+	hasProto: hasProto,
+	inBrowser: inBrowser,
+	devtools: devtools,
+	isIE: isIE,
+	isIE9: isIE9,
+	isAndroid: isAndroid,
+	isIOS: isIOS,
+	get transitionProp () { return transitionProp; },
+	get transitionEndEvent () { return transitionEndEvent; },
+	get animationProp () { return animationProp; },
+	get animationEndEvent () { return animationEndEvent; },
+	nextTick: nextTick,
+	get _Set () { return _Set; },
+	query: query,
+	inDoc: inDoc,
+	getAttr: getAttr,
+	getBindAttr: getBindAttr,
+	hasBindAttr: hasBindAttr,
+	before: before,
+	after: after,
+	remove: remove,
+	prepend: prepend,
+	replace: replace,
+	on: on,
+	off: off,
+	setClass: setClass,
+	addClass: addClass,
+	removeClass: removeClass,
+	extractContent: extractContent,
+	trimNode: trimNode,
+	isTemplate: isTemplate,
+	createAnchor: createAnchor,
+	findRef: findRef,
+	mapNodeRange: mapNodeRange,
+	removeNodeRange: removeNodeRange,
+	isFragment: isFragment,
+	getOuterHTML: getOuterHTML,
+	mergeOptions: mergeOptions,
+	resolveAsset: resolveAsset,
+	checkComponentAttr: checkComponentAttr,
+	commonTagRE: commonTagRE,
+	reservedTagRE: reservedTagRE,
+	get warn () { return warn; }
+});
+
+var uid = 0;
+
+function initMixin (Vue) {
+  /**
+   * The main init sequence. This is called for every
+   * instance, including ones that are created from extended
+   * constructors.
+   *
+   * @param {Object} options - this options object should be
+   *                           the result of merging class
+   *                           options and the options passed
+   *                           in to the constructor.
+   */
+
+  Vue.prototype._init = function (options) {
+    options = options || {};
+
+    this.$el = null;
+    this.$parent = options.parent;
+    this.$root = this.$parent ? this.$parent.$root : this;
+    this.$children = [];
+    this.$refs = {}; // child vm references
+    this.$els = {}; // element references
+    this._watchers = []; // all watchers as an array
+    this._directives = []; // all directives
+
+    // a uid
+    this._uid = uid++;
+
+    // a flag to avoid this being observed
+    this._isVue = true;
+
+    // events bookkeeping
+    this._events = {}; // registered callbacks
+    this._eventsCount = {}; // for $broadcast optimization
+
+    // fragment instance properties
+    this._isFragment = false;
+    this._fragment = // @type {DocumentFragment}
+    this._fragmentStart = // @type {Text|Comment}
+    this._fragmentEnd = null; // @type {Text|Comment}
+
+    // lifecycle state
+    this._isCompiled = this._isDestroyed = this._isReady = this._isAttached = this._isBeingDestroyed = this._vForRemoving = false;
+    this._unlinkFn = null;
+
+    // context:
+    // if this is a transcluded component, context
+    // will be the common parent vm of this instance
+    // and its host.
+    this._context = options._context || this.$parent;
+
+    // scope:
+    // if this is inside an inline v-for, the scope
+    // will be the intermediate scope created for this
+    // repeat fragment. this is used for linking props
+    // and container directives.
+    this._scope = options._scope;
+
+    // fragment:
+    // if this instance is compiled inside a Fragment, it
+    // needs to register itself as a child of that fragment
+    // for attach/detach to work properly.
+    this._frag = options._frag;
+    if (this._frag) {
+      this._frag.children.push(this);
+    }
+
+    // push self into parent / transclusion host
+    if (this.$parent) {
+      this.$parent.$children.push(this);
+    }
+
+    // merge options.
+    options = this.$options = mergeOptions(this.constructor.options, options, this);
+
+    // set ref
+    this._updateRef();
+
+    // initialize data as empty object.
+    // it will be filled up in _initData().
+    this._data = {};
+
+    // call init hook
+    this._callHook('init');
+
+    // initialize data observation and scope inheritance.
+    this._initState();
+
+    // setup event system and option events.
+    this._initEvents();
+
+    // call created hook
+    this._callHook('created');
+
+    // if `el` option is passed, start compilation.
+    if (options.el) {
+      this.$mount(options.el);
+    }
+  };
+}
+
+var pathCache = new Cache(1000);
+
+// actions
+var APPEND = 0;
+var PUSH = 1;
+var INC_SUB_PATH_DEPTH = 2;
+var PUSH_SUB_PATH = 3;
+
+// states
+var BEFORE_PATH = 0;
+var IN_PATH = 1;
+var BEFORE_IDENT = 2;
+var IN_IDENT = 3;
+var IN_SUB_PATH = 4;
+var IN_SINGLE_QUOTE = 5;
+var IN_DOUBLE_QUOTE = 6;
+var AFTER_PATH = 7;
+var ERROR = 8;
+
+var pathStateMachine = [];
+
+pathStateMachine[BEFORE_PATH] = {
+  'ws': [BEFORE_PATH],
+  'ident': [IN_IDENT, APPEND],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+};
+
+pathStateMachine[IN_PATH] = {
+  'ws': [IN_PATH],
+  '.': [BEFORE_IDENT],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+};
+
+pathStateMachine[BEFORE_IDENT] = {
+  'ws': [BEFORE_IDENT],
+  'ident': [IN_IDENT, APPEND]
+};
+
+pathStateMachine[IN_IDENT] = {
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND],
+  'ws': [IN_PATH, PUSH],
+  '.': [BEFORE_IDENT, PUSH],
+  '[': [IN_SUB_PATH, PUSH],
+  'eof': [AFTER_PATH, PUSH]
+};
+
+pathStateMachine[IN_SUB_PATH] = {
+  "'": [IN_SINGLE_QUOTE, APPEND],
+  '"': [IN_DOUBLE_QUOTE, APPEND],
+  '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
+  ']': [IN_PATH, PUSH_SUB_PATH],
+  'eof': ERROR,
+  'else': [IN_SUB_PATH, APPEND]
+};
+
+pathStateMachine[IN_SINGLE_QUOTE] = {
+  "'": [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_SINGLE_QUOTE, APPEND]
+};
+
+pathStateMachine[IN_DOUBLE_QUOTE] = {
+  '"': [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_DOUBLE_QUOTE, APPEND]
+};
+
+/**
+ * Determine the type of a character in a keypath.
+ *
+ * @param {Char} ch
+ * @return {String} type
+ */
+
+function getPathCharType(ch) {
+  if (ch === undefined) {
+    return 'eof';
+  }
+
+  var code = ch.charCodeAt(0);
+
+  switch (code) {
+    case 0x5B: // [
+    case 0x5D: // ]
+    case 0x2E: // .
+    case 0x22: // "
+    case 0x27: // '
+    case 0x30:
+      // 0
+      return ch;
+
+    case 0x5F: // _
+    case 0x24:
+      // $
+      return 'ident';
+
+    case 0x20: // Space
+    case 0x09: // Tab
+    case 0x0A: // Newline
+    case 0x0D: // Return
+    case 0xA0: // No-break space
+    case 0xFEFF: // Byte Order Mark
+    case 0x2028: // Line Separator
+    case 0x2029:
+      // Paragraph Separator
+      return 'ws';
+  }
+
+  // a-z, A-Z
+  if (code >= 0x61 && code <= 0x7A || code >= 0x41 && code <= 0x5A) {
+    return 'ident';
+  }
+
+  // 1-9
+  if (code >= 0x31 && code <= 0x39) {
+    return 'number';
+  }
+
+  return 'else';
+}
+
+/**
+ * Format a subPath, return its plain form if it is
+ * a literal string or number. Otherwise prepend the
+ * dynamic indicator (*).
+ *
+ * @param {String} path
+ * @return {String}
+ */
+
+function formatSubPath(path) {
+  var trimmed = path.trim();
+  // invalid leading 0
+  if (path.charAt(0) === '0' && isNaN(path)) {
+    return false;
+  }
+  return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed;
+}
+
+/**
+ * Parse a string path into an array of segments
+ *
+ * @param {String} path
+ * @return {Array|undefined}
+ */
+
+function parse(path) {
+  var keys = [];
+  var index = -1;
+  var mode = BEFORE_PATH;
+  var subPathDepth = 0;
+  var c, newChar, key, type, transition, action, typeMap;
+
+  var actions = [];
+
+  actions[PUSH] = function () {
+    if (key !== undefined) {
+      keys.push(key);
+      key = undefined;
+    }
+  };
+
+  actions[APPEND] = function () {
+    if (key === undefined) {
+      key = newChar;
+    } else {
+      key += newChar;
+    }
+  };
+
+  actions[INC_SUB_PATH_DEPTH] = function () {
+    actions[APPEND]();
+    subPathDepth++;
+  };
+
+  actions[PUSH_SUB_PATH] = function () {
+    if (subPathDepth > 0) {
+      subPathDepth--;
+      mode = IN_SUB_PATH;
+      actions[APPEND]();
+    } else {
+      subPathDepth = 0;
+      key = formatSubPath(key);
+      if (key === false) {
+        return false;
+      } else {
+        actions[PUSH]();
+      }
+    }
+  };
+
+  function maybeUnescapeQuote() {
+    var nextChar = path[index + 1];
+    if (mode === IN_SINGLE_QUOTE && nextChar === "'" || mode === IN_DOUBLE_QUOTE && nextChar === '"') {
+      index++;
+      newChar = '\\' + nextChar;
+      actions[APPEND]();
+      return true;
+    }
+  }
+
+  while (mode != null) {
+    index++;
+    c = path[index];
+
+    if (c === '\\' && maybeUnescapeQuote()) {
+      continue;
+    }
+
+    type = getPathCharType(c);
+    typeMap = pathStateMachine[mode];
+    transition = typeMap[type] || typeMap['else'] || ERROR;
+
+    if (transition === ERROR) {
+      return; // parse error
+    }
+
+    mode = transition[0];
+    action = actions[transition[1]];
+    if (action) {
+      newChar = transition[2];
+      newChar = newChar === undefined ? c : newChar;
+      if (action() === false) {
+        return;
+      }
+    }
+
+    if (mode === AFTER_PATH) {
+      keys.raw = path;
+      return keys;
+    }
+  }
+}
+
+/**
+ * External parse that check for a cache hit first
+ *
+ * @param {String} path
+ * @return {Array|undefined}
+ */
+
+function parsePath(path) {
+  var hit = pathCache.get(path);
+  if (!hit) {
+    hit = parse(path);
+    if (hit) {
+      pathCache.put(path, hit);
+    }
+  }
+  return hit;
+}
+
+/**
+ * Get from an object from a path string
+ *
+ * @param {Object} obj
+ * @param {String} path
+ */
+
+function getPath(obj, path) {
+  return parseExpression$1(path).get(obj);
+}
+
+/**
+ * Warn against setting non-existent root path on a vm.
+ */
+
+var warnNonExistent;
+if ('development' !== 'production') {
+  warnNonExistent = function (path, vm) {
+    warn('You are setting a non-existent path "' + path.raw + '" ' + 'on a vm instance. Consider pre-initializing the property ' + 'with the "data" option for more reliable reactivity ' + 'and better performance.', vm);
+  };
+}
+
+/**
+ * Set on an object from a path
+ *
+ * @param {Object} obj
+ * @param {String | Array} path
+ * @param {*} val
+ */
+
+function setPath(obj, path, val) {
+  var original = obj;
+  if (typeof path === 'string') {
+    path = parse(path);
+  }
+  if (!path || !isObject(obj)) {
+    return false;
+  }
+  var last, key;
+  for (var i = 0, l = path.length; i < l; i++) {
+    last = obj;
+    key = path[i];
+    if (key.charAt(0) === '*') {
+      key = parseExpression$1(key.slice(1)).get.call(original, original);
+    }
+    if (i < l - 1) {
+      obj = obj[key];
+      if (!isObject(obj)) {
+        obj = {};
+        if ('development' !== 'production' && last._isVue) {
+          warnNonExistent(path, last);
+        }
+        set(last, key, obj);
+      }
+    } else {
+      if (isArray(obj)) {
+        obj.$set(key, val);
+      } else if (key in obj) {
+        obj[key] = val;
+      } else {
+        if ('development' !== 'production' && obj._isVue) {
+          warnNonExistent(path, obj);
+        }
+        set(obj, key, val);
+      }
+    }
+  }
+  return true;
+}
+
+var path = Object.freeze({
+  parsePath: parsePath,
+  getPath: getPath,
+  setPath: setPath
+});
+
+var expressionCache = new Cache(1000);
+
+var allowedKeywords = 'Math,Date,this,true,false,null,undefined,Infinity,NaN,' + 'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,' + 'encodeURIComponent,parseInt,parseFloat';
+var allowedKeywordsRE = new RegExp('^(' + allowedKeywords.replace(/,/g, '\\b|') + '\\b)');
+
+// keywords that don't make sense inside expressions
+var improperKeywords = 'break,case,class,catch,const,continue,debugger,default,' + 'delete,do,else,export,extends,finally,for,function,if,' + 'import,in,instanceof,let,return,super,switch,throw,try,' + 'var,while,with,yield,enum,await,implements,package,' + 'protected,static,interface,private,public';
+var improperKeywordsRE = new RegExp('^(' + improperKeywords.replace(/,/g, '\\b|') + '\\b)');
+
+var wsRE = /\s/g;
+var newlineRE = /\n/g;
+var saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\"']|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g;
+var restoreRE = /"(\d+)"/g;
+var pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/;
+var identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g;
+var literalValueRE$1 = /^(?:true|false|null|undefined|Infinity|NaN)$/;
+
+function noop() {}
+
+/**
+ * Save / Rewrite / Restore
+ *
+ * When rewriting paths found in an expression, it is
+ * possible for the same letter sequences to be found in
+ * strings and Object literal property keys. Therefore we
+ * remove and store these parts in a temporary array, and
+ * restore them after the path rewrite.
+ */
+
+var saved = [];
+
+/**
+ * Save replacer
+ *
+ * The save regex can match two possible cases:
+ * 1. An opening object literal
+ * 2. A string
+ * If matched as a plain string, we need to escape its
+ * newlines, since the string needs to be preserved when
+ * generating the function body.
+ *
+ * @param {String} str
+ * @param {String} isString - str if matched as a string
+ * @return {String} - placeholder with index
+ */
+
+function save(str, isString) {
+  var i = saved.length;
+  saved[i] = isString ? str.replace(newlineRE, '\\n') : str;
+  return '"' + i + '"';
+}
+
+/**
+ * Path rewrite replacer
+ *
+ * @param {String} raw
+ * @return {String}
+ */
+
+function rewrite(raw) {
+  var c = raw.charAt(0);
+  var path = raw.slice(1);
+  if (allowedKeywordsRE.test(path)) {
+    return raw;
+  } else {
+    path = path.indexOf('"') > -1 ? path.replace(restoreRE, restore) : path;
+    return c + 'scope.' + path;
+  }
+}
+
+/**
+ * Restore replacer
+ *
+ * @param {String} str
+ * @param {String} i - matched save index
+ * @return {String}
+ */
+
+function restore(str, i) {
+  return saved[i];
+}
+
+/**
+ * Rewrite an expression, prefixing all path accessors with
+ * `scope.` and generate getter/setter functions.
+ *
+ * @param {String} exp
+ * @return {Function}
+ */
+
+function compileGetter(exp) {
+  if (improperKeywordsRE.test(exp)) {
+    'development' !== 'production' && warn('Avoid using reserved keywords in expression: ' + exp);
+  }
+  // reset state
+  saved.length = 0;
+  // save strings and object literal keys
+  var body = exp.replace(saveRE, save).replace(wsRE, '');
+  // rewrite all paths
+  // pad 1 space here because the regex matches 1 extra char
+  body = (' ' + body).replace(identRE, rewrite).replace(restoreRE, restore);
+  return makeGetterFn(body);
+}
+
+/**
+ * Build a getter function. Requires eval.
+ *
+ * We isolate the try/catch so it doesn't affect the
+ * optimization of the parse function when it is not called.
+ *
+ * @param {String} body
+ * @return {Function|undefined}
+ */
+
+function makeGetterFn(body) {
+  try {
+    /* eslint-disable no-new-func */
+    return new Function('scope', 'return ' + body + ';');
+    /* eslint-enable no-new-func */
+  } catch (e) {
+    if ('development' !== 'production') {
+      /* istanbul ignore if */
+      if (e.toString().match(/unsafe-eval|CSP/)) {
+        warn('It seems you are using the default build of Vue.js in an environment ' + 'with Content Security Policy that prohibits unsafe-eval. ' + 'Use the CSP-compliant build instead: ' + 'http://vuejs.org/guide/installation.html#CSP-compliant-build');
+      } else {
+        warn('Invalid expression. ' + 'Generated function body: ' + body);
+      }
+    }
+    return noop;
+  }
+}
+
+/**
+ * Compile a setter function for the expression.
+ *
+ * @param {String} exp
+ * @return {Function|undefined}
+ */
+
+function compileSetter(exp) {
+  var path = parsePath(exp);
+  if (path) {
+    return function (scope, val) {
+      setPath(scope, path, val);
+    };
+  } else {
+    'development' !== 'production' && warn('Invalid setter expression: ' + exp);
+  }
+}
+
+/**
+ * Parse an expression into re-written getter/setters.
+ *
+ * @param {String} exp
+ * @param {Boolean} needSet
+ * @return {Function}
+ */
+
+function parseExpression$1(exp, needSet) {
+  exp = exp.trim();
+  // try cache
+  var hit = expressionCache.get(exp);
+  if (hit) {
+    if (needSet && !hit.set) {
+      hit.set = compileSetter(hit.exp);
+    }
+    return hit;
+  }
+  var res = { exp: exp };
+  res.get = isSimplePath(exp) && exp.indexOf('[') < 0
+  // optimized super simple getter
+  ? makeGetterFn('scope.' + exp)
+  // dynamic getter
+  : compileGetter(exp);
+  if (needSet) {
+    res.set = compileSetter(exp);
+  }
+  expressionCache.put(exp, res);
+  return res;
+}
+
+/**
+ * Check if an expression is a simple path.
+ *
+ * @param {String} exp
+ * @return {Boolean}
+ */
+
+function isSimplePath(exp) {
+  return pathTestRE.test(exp) &&
+  // don't treat literal values as paths
+  !literalValueRE$1.test(exp) &&
+  // Math constants e.g. Math.PI, Math.E etc.
+  exp.slice(0, 5) !== 'Math.';
+}
+
+var expression = Object.freeze({
+  parseExpression: parseExpression$1,
+  isSimplePath: isSimplePath
+});
+
+// we have two separate queues: one for directive updates
+// and one for user watcher registered via $watch().
+// we want to guarantee directive updates to be called
+// before user watchers so that when user watchers are
+// triggered, the DOM would have already been in updated
+// state.
+
+var queue = [];
+var userQueue = [];
+var has = {};
+var circular = {};
+var waiting = false;
+
+/**
+ * Reset the batcher's state.
+ */
+
+function resetBatcherState() {
+  queue.length = 0;
+  userQueue.length = 0;
+  has = {};
+  circular = {};
+  waiting = false;
+}
+
+/**
+ * Flush both queues and run the watchers.
+ */
+
+function flushBatcherQueue() {
+  var _again = true;
+
+  _function: while (_again) {
+    _again = false;
+
+    runBatcherQueue(queue);
+    runBatcherQueue(userQueue);
+    // user watchers triggered more watchers,
+    // keep flushing until it depletes
+    if (queue.length) {
+      _again = true;
+      continue _function;
+    }
+    // dev tool hook
+    /* istanbul ignore if */
+    if (devtools && config.devtools) {
+      devtools.emit('flush');
+    }
+    resetBatcherState();
+  }
+}
+
+/**
+ * Run the watchers in a single queue.
+ *
+ * @param {Array} queue
+ */
+
+function runBatcherQueue(queue) {
+  // do not cache length because more watchers might be pushed
+  // as we run existing watchers
+  for (var i = 0; i < queue.length; i++) {
+    var watcher = queue[i];
+    var id = watcher.id;
+    has[id] = null;
+    watcher.run();
+    // in dev build, check and stop circular updates.
+    if ('development' !== 'production' && has[id] != null) {
+      circular[id] = (circular[id] || 0) + 1;
+      if (circular[id] > config._maxUpdateCount) {
+        warn('You may have an infinite update loop for watcher ' + 'with expression "' + watcher.expression + '"', watcher.vm);
+        break;
+      }
+    }
+  }
+  queue.length = 0;
+}
+
+/**
+ * Push a watcher into the watcher queue.
+ * Jobs with duplicate IDs will be skipped unless it's
+ * pushed when the queue is being flushed.
+ *
+ * @param {Watcher} watcher
+ *   properties:
+ *   - {Number} id
+ *   - {Function} run
+ */
+
+function pushWatcher(watcher) {
+  var id = watcher.id;
+  if (has[id] == null) {
+    // push watcher into appropriate queue
+    var q = watcher.user ? userQueue : queue;
+    has[id] = q.length;
+    q.push(watcher);
+    // queue the flush
+    if (!waiting) {
+      waiting = true;
+      nextTick(flushBatcherQueue);
+    }
+  }
+}
+
+var uid$2 = 0;
+
+/**
+ * A watcher parses an expression, collects dependencies,
+ * and fires callback when the expression value changes.
+ * This is used for both the $watch() api and directives.
+ *
+ * @param {Vue} vm
+ * @param {String|Function} expOrFn
+ * @param {Function} cb
+ * @param {Object} options
+ *                 - {Array} filters
+ *                 - {Boolean} twoWay
+ *                 - {Boolean} deep
+ *                 - {Boolean} user
+ *                 - {Boolean} sync
+ *                 - {Boolean} lazy
+ *                 - {Function} [preProcess]
+ *                 - {Function} [postProcess]
+ * @constructor
+ */
+function Watcher(vm, expOrFn, cb, options) {
+  // mix in options
+  if (options) {
+    extend(this, options);
+  }
+  var isFn = typeof expOrFn === 'function';
+  this.vm = vm;
+  vm._watchers.push(this);
+  this.expression = expOrFn;
+  this.cb = cb;
+  this.id = ++uid$2; // uid for batching
+  this.active = true;
+  this.dirty = this.lazy; // for lazy watchers
+  this.deps = [];
+  this.newDeps = [];
+  this.depIds = new _Set();
+  this.newDepIds = new _Set();
+  this.prevError = null; // for async error stacks
+  // parse expression for getter/setter
+  if (isFn) {
+    this.getter = expOrFn;
+    this.setter = undefined;
+  } else {
+    var res = parseExpression$1(expOrFn, this.twoWay);
+    this.getter = res.get;
+    this.setter = res.set;
+  }
+  this.value = this.lazy ? undefined : this.get();
+  // state for avoiding false triggers for deep and Array
+  // watchers during vm._digest()
+  this.queued = this.shallow = false;
+}
+
+/**
+ * Evaluate the getter, and re-collect dependencies.
+ */
+
+Watcher.prototype.get = function () {
+  this.beforeGet();
+  var scope = this.scope || this.vm;
+  var value;
+  try {
+    value = this.getter.call(scope, scope);
+  } catch (e) {
+    if ('development' !== 'production' && config.warnExpressionErrors) {
+      warn('Error when evaluating expression ' + '"' + this.expression + '": ' + e.toString(), this.vm);
+    }
+  }
+  // "touch" every property so they are all tracked as
+  // dependencies for deep watching
+  if (this.deep) {
+    traverse(value);
+  }
+  if (this.preProcess) {
+    value = this.preProcess(value);
+  }
+  if (this.filters) {
+    value = scope._applyFilters(value, null, this.filters, false);
+  }
+  if (this.postProcess) {
+    value = this.postProcess(value);
+  }
+  this.afterGet();
+  return value;
+};
+
+/**
+ * Set the corresponding value with the setter.
+ *
+ * @param {*} value
+ */
+
+Watcher.prototype.set = function (value) {
+  var scope = this.scope || this.vm;
+  if (this.filters) {
+    value = scope._applyFilters(value, this.value, this.filters, true);
+  }
+  try {
+    this.setter.call(scope, scope, value);
+  } catch (e) {
+    if ('development' !== 'production' && config.warnExpressionErrors) {
+      warn('Error when evaluating setter ' + '"' + this.expression + '": ' + e.toString(), this.vm);
+    }
+  }
+  // two-way sync for v-for alias
+  var forContext = scope.$forContext;
+  if (forContext && forContext.alias === this.expression) {
+    if (forContext.filters) {
+      'development' !== 'production' && warn('It seems you are using two-way binding on ' + 'a v-for alias (' + this.expression + '), and the ' + 'v-for has filters. This will not work properly. ' + 'Either remove the filters or use an array of ' + 'objects and bind to object properties instead.', this.vm);
+      return;
+    }
+    forContext._withLock(function () {
+      if (scope.$key) {
+        // original is an object
+        forContext.rawValue[scope.$key] = value;
+      } else {
+        forContext.rawValue.$set(scope.$index, value);
+      }
+    });
+  }
+};
+
+/**
+ * Prepare for dependency collection.
+ */
+
+Watcher.prototype.beforeGet = function () {
+  Dep.target = this;
+};
+
+/**
+ * Add a dependency to this directive.
+ *
+ * @param {Dep} dep
+ */
+
+Watcher.prototype.addDep = function (dep) {
+  var id = dep.id;
+  if (!this.newDepIds.has(id)) {
+    this.newDepIds.add(id);
+    this.newDeps.push(dep);
+    if (!this.depIds.has(id)) {
+      dep.addSub(this);
+    }
+  }
+};
+
+/**
+ * Clean up for dependency collection.
+ */
+
+Watcher.prototype.afterGet = function () {
+  Dep.target = null;
+  var i = this.deps.length;
+  while (i--) {
+    var dep = this.deps[i];
+    if (!this.newDepIds.has(dep.id)) {
+      dep.removeSub(this);
+    }
+  }
+  var tmp = this.depIds;
+  this.depIds = this.newDepIds;
+  this.newDepIds = tmp;
+  this.newDepIds.clear();
+  tmp = this.deps;
+  this.deps = this.newDeps;
+  this.newDeps = tmp;
+  this.newDeps.length = 0;
+};
+
+/**
+ * Subscriber interface.
+ * Will be called when a dependency changes.
+ *
+ * @param {Boolean} shallow
+ */
+
+Watcher.prototype.update = function (shallow) {
+  if (this.lazy) {
+    this.dirty = true;
+  } else if (this.sync || !config.async) {
+    this.run();
+  } else {
+    // if queued, only overwrite shallow with non-shallow,
+    // but not the other way around.
+    this.shallow = this.queued ? shallow ? this.shallow : false : !!shallow;
+    this.queued = true;
+    // record before-push error stack in debug mode
+    /* istanbul ignore if */
+    if ('development' !== 'production' && config.debug) {
+      this.prevError = new Error('[vue] async stack trace');
+    }
+    pushWatcher(this);
+  }
+};
+
+/**
+ * Batcher job interface.
+ * Will be called by the batcher.
+ */
+
+Watcher.prototype.run = function () {
+  if (this.active) {
+    var value = this.get();
+    if (value !== this.value ||
+    // Deep watchers and watchers on Object/Arrays should fire even
+    // when the value is the same, because the value may
+    // have mutated; but only do so if this is a
+    // non-shallow update (caused by a vm digest).
+    (isObject(value) || this.deep) && !this.shallow) {
+      // set new value
+      var oldValue = this.value;
+      this.value = value;
+      // in debug + async mode, when a watcher callbacks
+      // throws, we also throw the saved before-push error
+      // so the full cross-tick stack trace is available.
+      var prevError = this.prevError;
+      /* istanbul ignore if */
+      if ('development' !== 'production' && config.debug && prevError) {
+        this.prevError = null;
+        try {
+          this.cb.call(this.vm, value, oldValue);
+        } catch (e) {
+          nextTick(function () {
+            throw prevError;
+          }, 0);
+          throw e;
+        }
+      } else {
+        this.cb.call(this.vm, value, oldValue);
+      }
+    }
+    this.queued = this.shallow = false;
+  }
+};
+
+/**
+ * Evaluate the value of the watcher.
+ * This only gets called for lazy watchers.
+ */
+
+Watcher.prototype.evaluate = function () {
+  // avoid overwriting another watcher that is being
+  // collected.
+  var current = Dep.target;
+  this.value = this.get();
+  this.dirty = false;
+  Dep.target = current;
+};
+
+/**
+ * Depend on all deps collected by this watcher.
+ */
+
+Watcher.prototype.depend = function () {
+  var i = this.deps.length;
+  while (i--) {
+    this.deps[i].depend();
+  }
+};
+
+/**
+ * Remove self from all dependencies' subcriber list.
+ */
+
+Watcher.prototype.teardown = function () {
+  if (this.active) {
+    // remove self from vm's watcher list
+    // this is a somewhat expensive operation so we skip it
+    // if the vm is being destroyed or is performing a v-for
+    // re-render (the watcher list is then filtered by v-for).
+    if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) {
+      this.vm._watchers.$remove(this);
+    }
+    var i = this.deps.length;
+    while (i--) {
+      this.deps[i].removeSub(this);
+    }
+    this.active = false;
+    this.vm = this.cb = this.value = null;
+  }
+};
+
+/**
+ * Recrusively traverse an object to evoke all converted
+ * getters, so that every nested property inside the object
+ * is collected as a "deep" dependency.
+ *
+ * @param {*} val
+ */
+
+var seenObjects = new _Set();
+function traverse(val, seen) {
+  var i = undefined,
+      keys = undefined;
+  if (!seen) {
+    seen = seenObjects;
+    seen.clear();
+  }
+  var isA = isArray(val);
+  var isO = isObject(val);
+  if ((isA || isO) && Object.isExtensible(val)) {
+    if (val.__ob__) {
+      var depId = val.__ob__.dep.id;
+      if (seen.has(depId)) {
+        return;
+      } else {
+        seen.add(depId);
+      }
+    }
+    if (isA) {
+      i = val.length;
+      while (i--) traverse(val[i], seen);
+    } else if (isO) {
+      keys = Object.keys(val);
+      i = keys.length;
+      while (i--) traverse(val[keys[i]], seen);
+    }
+  }
+}
+
+var text$1 = {
+
+  bind: function bind() {
+    this.attr = this.el.nodeType === 3 ? 'data' : 'textContent';
+  },
+
+  update: function update(value) {
+    this.el[this.attr] = _toString(value);
+  }
+};
+
+var templateCache = new Cache(1000);
+var idSelectorCache = new Cache(1000);
+
+var map = {
+  efault: [0, '', ''],
+  legend: [1, '<fieldset>', '</fieldset>'],
+  tr: [2, '<table><tbody>', '</tbody></table>'],
+  col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>']
+};
+
+map.td = map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'];
+
+map.option = map.optgroup = [1, '<select multiple="multiple">', '</select>'];
+
+map.thead = map.tbody = map.colgroup = map.caption = map.tfoot = [1, '<table>', '</table>'];
+
+map.g = map.defs = map.symbol = map.use = map.image = map.text = map.circle = map.ellipse = map.line = map.path = map.polygon = map.polyline = map.rect = [1, '<svg ' + 'xmlns="http://www.w3.org/2000/svg" ' + 'xmlns:xlink="http://www.w3.org/1999/xlink" ' + 'xmlns:ev="http://www.w3.org/2001/xml-events"' + 'version="1.1">', '</svg>'];
+
+/**
+ * Check if a node is a supported template node with a
+ * DocumentFragment content.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+function isRealTemplate(node) {
+  return isTemplate(node) && isFragment(node.content);
+}
+
+var tagRE$1 = /<([\w:-]+)/;
+var entityRE = /&#?\w+?;/;
+var commentRE = /<!--/;
+
+/**
+ * Convert a string template to a DocumentFragment.
+ * Determines correct wrapping by tag types. Wrapping
+ * strategy found in jQuery & component/domify.
+ *
+ * @param {String} templateString
+ * @param {Boolean} raw
+ * @return {DocumentFragment}
+ */
+
+function stringToFragment(templateString, raw) {
+  // try a cache hit first
+  var cacheKey = raw ? templateString : templateString.trim();
+  var hit = templateCache.get(cacheKey);
+  if (hit) {
+    return hit;
+  }
+
+  var frag = document.createDocumentFragment();
+  var tagMatch = templateString.match(tagRE$1);
+  var entityMatch = entityRE.test(templateString);
+  var commentMatch = commentRE.test(templateString);
+
+  if (!tagMatch && !entityMatch && !commentMatch) {
+    // text only, return a single text node.
+    frag.appendChild(document.createTextNode(templateString));
+  } else {
+    var tag = tagMatch && tagMatch[1];
+    var wrap = map[tag] || map.efault;
+    var depth = wrap[0];
+    var prefix = wrap[1];
+    var suffix = wrap[2];
+    var node = document.createElement('div');
+
+    node.innerHTML = prefix + templateString + suffix;
+    while (depth--) {
+      node = node.lastChild;
+    }
+
+    var child;
+    /* eslint-disable no-cond-assign */
+    while (child = node.firstChild) {
+      /* eslint-enable no-cond-assign */
+      frag.appendChild(child);
+    }
+  }
+  if (!raw) {
+    trimNode(frag);
+  }
+  templateCache.put(cacheKey, frag);
+  return frag;
+}
+
+/**
+ * Convert a template node to a DocumentFragment.
+ *
+ * @param {Node} node
+ * @return {DocumentFragment}
+ */
+
+function nodeToFragment(node) {
+  // if its a template tag and the browser supports it,
+  // its content is already a document fragment. However, iOS Safari has
+  // bug when using directly cloned template content with touch
+  // events and can cause crashes when the nodes are removed from DOM, so we
+  // have to treat template elements as string templates. (#2805)
+  /* istanbul ignore if */
+  if (isRealTemplate(node)) {
+    return stringToFragment(node.innerHTML);
+  }
+  // script template
+  if (node.tagName === 'SCRIPT') {
+    return stringToFragment(node.textContent);
+  }
+  // normal node, clone it to avoid mutating the original
+  var clonedNode = cloneNode(node);
+  var frag = document.createDocumentFragment();
+  var child;
+  /* eslint-disable no-cond-assign */
+  while (child = clonedNode.firstChild) {
+    /* eslint-enable no-cond-assign */
+    frag.appendChild(child);
+  }
+  trimNode(frag);
+  return frag;
+}
+
+// Test for the presence of the Safari template cloning bug
+// https://bugs.webkit.org/showug.cgi?id=137755
+var hasBrokenTemplate = (function () {
+  /* istanbul ignore else */
+  if (inBrowser) {
+    var a = document.createElement('div');
+    a.innerHTML = '<template>1</template>';
+    return !a.cloneNode(true).firstChild.innerHTML;
+  } else {
+    return false;
+  }
+})();
+
+// Test for IE10/11 textarea placeholder clone bug
+var hasTextareaCloneBug = (function () {
+  /* istanbul ignore else */
+  if (inBrowser) {
+    var t = document.createElement('textarea');
+    t.placeholder = 't';
+    return t.cloneNode(true).value === 't';
+  } else {
+    return false;
+  }
+})();
+
+/**
+ * 1. Deal with Safari cloning nested <template> bug by
+ *    manually cloning all template instances.
+ * 2. Deal with IE10/11 textarea placeholder bug by setting
+ *    the correct value after cloning.
+ *
+ * @param {Element|DocumentFragment} node
+ * @return {Element|DocumentFragment}
+ */
+
+function cloneNode(node) {
+  /* istanbul ignore if */
+  if (!node.querySelectorAll) {
+    return node.cloneNode();
+  }
+  var res = node.cloneNode(true);
+  var i, original, cloned;
+  /* istanbul ignore if */
+  if (hasBrokenTemplate) {
+    var tempClone = res;
+    if (isRealTemplate(node)) {
+      node = node.content;
+      tempClone = res.content;
+    }
+    original = node.querySelectorAll('template');
+    if (original.length) {
+      cloned = tempClone.querySelectorAll('template');
+      i = cloned.length;
+      while (i--) {
+        cloned[i].parentNode.replaceChild(cloneNode(original[i]), cloned[i]);
+      }
+    }
+  }
+  /* istanbul ignore if */
+  if (hasTextareaCloneBug) {
+    if (node.tagName === 'TEXTAREA') {
+      res.value = node.value;
+    } else {
+      original = node.querySelectorAll('textarea');
+      if (original.length) {
+        cloned = res.querySelectorAll('textarea');
+        i = cloned.length;
+        while (i--) {
+          cloned[i].value = original[i].value;
+        }
+      }
+    }
+  }
+  return res;
+}
+
+/**
+ * Process the template option and normalizes it into a
+ * a DocumentFragment that can be used as a partial or a
+ * instance template.
+ *
+ * @param {*} template
+ *        Possible values include:
+ *        - DocumentFragment object
+ *        - Node object of type Template
+ *        - id selector: '#some-template-id'
+ *        - template string: '<div><span>{{msg}}</span></div>'
+ * @param {Boolean} shouldClone
+ * @param {Boolean} raw
+ *        inline HTML interpolation. Do not check for id
+ *        selector and keep whitespace in the string.
+ * @return {DocumentFragment|undefined}
+ */
+
+function parseTemplate(template, shouldClone, raw) {
+  var node, frag;
+
+  // if the template is already a document fragment,
+  // do nothing
+  if (isFragment(template)) {
+    trimNode(template);
+    return shouldClone ? cloneNode(template) : template;
+  }
+
+  if (typeof template === 'string') {
+    // id selector
+    if (!raw && template.charAt(0) === '#') {
+      // id selector can be cached too
+      frag = idSelectorCache.get(template);
+      if (!frag) {
+        node = document.getElementById(template.slice(1));
+        if (node) {
+          frag = nodeToFragment(node);
+          // save selector to cache
+          idSelectorCache.put(template, frag);
+        }
+      }
+    } else {
+      // normal string template
+      frag = stringToFragment(template, raw);
+    }
+  } else if (template.nodeType) {
+    // a direct node
+    frag = nodeToFragment(template);
+  }
+
+  return frag && shouldClone ? cloneNode(frag) : frag;
+}
+
+var template = Object.freeze({
+  cloneNode: cloneNode,
+  parseTemplate: parseTemplate
+});
+
+var html = {
+
+  bind: function bind() {
+    // a comment node means this is a binding for
+    // {{{ inline unescaped html }}}
+    if (this.el.nodeType === 8) {
+      // hold nodes
+      this.nodes = [];
+      // replace the placeholder with proper anchor
+      this.anchor = createAnchor('v-html');
+      replace(this.el, this.anchor);
+    }
+  },
+
+  update: function update(value) {
+    value = _toString(value);
+    if (this.nodes) {
+      this.swap(value);
+    } else {
+      this.el.innerHTML = value;
+    }
+  },
+
+  swap: function swap(value) {
+    // remove old nodes
+    var i = this.nodes.length;
+    while (i--) {
+      remove(this.nodes[i]);
+    }
+    // convert new value to a fragment
+    // do not attempt to retrieve from id selector
+    var frag = parseTemplate(value, true, true);
+    // save a reference to these nodes so we can remove later
+    this.nodes = toArray(frag.childNodes);
+    before(frag, this.anchor);
+  }
+};
+
+/**
+ * Abstraction for a partially-compiled fragment.
+ * Can optionally compile content with a child scope.
+ *
+ * @param {Function} linker
+ * @param {Vue} vm
+ * @param {DocumentFragment} frag
+ * @param {Vue} [host]
+ * @param {Object} [scope]
+ * @param {Fragment} [parentFrag]
+ */
+function Fragment(linker, vm, frag, host, scope, parentFrag) {
+  this.children = [];
+  this.childFrags = [];
+  this.vm = vm;
+  this.scope = scope;
+  this.inserted = false;
+  this.parentFrag = parentFrag;
+  if (parentFrag) {
+    parentFrag.childFrags.push(this);
+  }
+  this.unlink = linker(vm, frag, host, scope, this);
+  var single = this.single = frag.childNodes.length === 1 &&
+  // do not go single mode if the only node is an anchor
+  !frag.childNodes[0].__v_anchor;
+  if (single) {
+    this.node = frag.childNodes[0];
+    this.before = singleBefore;
+    this.remove = singleRemove;
+  } else {
+    this.node = createAnchor('fragment-start');
+    this.end = createAnchor('fragment-end');
+    this.frag = frag;
+    prepend(this.node, frag);
+    frag.appendChild(this.end);
+    this.before = multiBefore;
+    this.remove = multiRemove;
+  }
+  this.node.__v_frag = this;
+}
+
+/**
+ * Call attach/detach for all components contained within
+ * this fragment. Also do so recursively for all child
+ * fragments.
+ *
+ * @param {Function} hook
+ */
+
+Fragment.prototype.callHook = function (hook) {
+  var i, l;
+  for (i = 0, l = this.childFrags.length; i < l; i++) {
+    this.childFrags[i].callHook(hook);
+  }
+  for (i = 0, l = this.children.length; i < l; i++) {
+    hook(this.children[i]);
+  }
+};
+
+/**
+ * Insert fragment before target, single node version
+ *
+ * @param {Node} target
+ * @param {Boolean} withTransition
+ */
+
+function singleBefore(target, withTransition) {
+  this.inserted = true;
+  var method = withTransition !== false ? beforeWithTransition : before;
+  method(this.node, target, this.vm);
+  if (inDoc(this.node)) {
+    this.callHook(attach);
+  }
+}
+
+/**
+ * Remove fragment, single node version
+ */
+
+function singleRemove() {
+  this.inserted = false;
+  var shouldCallRemove = inDoc(this.node);
+  var self = this;
+  this.beforeRemove();
+  removeWithTransition(this.node, this.vm, function () {
+    if (shouldCallRemove) {
+      self.callHook(detach);
+    }
+    self.destroy();
+  });
+}
+
+/**
+ * Insert fragment before target, multi-nodes version
+ *
+ * @param {Node} target
+ * @param {Boolean} withTransition
+ */
+
+function multiBefore(target, withTransition) {
+  this.inserted = true;
+  var vm = this.vm;
+  var method = withTransition !== false ? beforeWithTransition : before;
+  mapNodeRange(this.node, this.end, function (node) {
+    method(node, target, vm);
+  });
+  if (inDoc(this.node)) {
+    this.callHook(attach);
+  }
+}
+
+/**
+ * Remove fragment, multi-nodes version
+ */
+
+function multiRemove() {
+  this.inserted = false;
+  var self = this;
+  var shouldCallRemove = inDoc(this.node);
+  this.beforeRemove();
+  removeNodeRange(this.node, this.end, this.vm, this.frag, function () {
+    if (shouldCallRemove) {
+      self.callHook(detach);
+    }
+    self.destroy();
+  });
+}
+
+/**
+ * Prepare the fragment for removal.
+ */
+
+Fragment.prototype.beforeRemove = function () {
+  var i, l;
+  for (i = 0, l = this.childFrags.length; i < l; i++) {
+    // call the same method recursively on child
+    // fragments, depth-first
+    this.childFrags[i].beforeRemove(false);
+  }
+  for (i = 0, l = this.children.length; i < l; i++) {
+    // Call destroy for all contained instances,
+    // with remove:false and defer:true.
+    // Defer is necessary because we need to
+    // keep the children to call detach hooks
+    // on them.
+    this.children[i].$destroy(false, true);
+  }
+  var dirs = this.unlink.dirs;
+  for (i = 0, l = dirs.length; i < l; i++) {
+    // disable the watchers on all the directives
+    // so that the rendered content stays the same
+    // during removal.
+    dirs[i]._watcher && dirs[i]._watcher.teardown();
+  }
+};
+
+/**
+ * Destroy the fragment.
+ */
+
+Fragment.prototype.destroy = function () {
+  if (this.parentFrag) {
+    this.parentFrag.childFrags.$remove(this);
+  }
+  this.node.__v_frag = null;
+  this.unlink();
+};
+
+/**
+ * Call attach hook for a Vue instance.
+ *
+ * @param {Vue} child
+ */
+
+function attach(child) {
+  if (!child._isAttached && inDoc(child.$el)) {
+    child._callHook('attached');
+  }
+}
+
+/**
+ * Call detach hook for a Vue instance.
+ *
+ * @param {Vue} child
+ */
+
+function detach(child) {
+  if (child._isAttached && !inDoc(child.$el)) {
+    child._callHook('detached');
+  }
+}
+
+var linkerCache = new Cache(5000);
+
+/**
+ * A factory that can be used to create instances of a
+ * fragment. Caches the compiled linker if possible.
+ *
+ * @param {Vue} vm
+ * @param {Element|String} el
+ */
+function FragmentFactory(vm, el) {
+  this.vm = vm;
+  var template;
+  var isString = typeof el === 'string';
+  if (isString || isTemplate(el) && !el.hasAttribute('v-if')) {
+    template = parseTemplate(el, true);
+  } else {
+    template = document.createDocumentFragment();
+    template.appendChild(el);
+  }
+  this.template = template;
+  // linker can be cached, but only for components
+  var linker;
+  var cid = vm.constructor.cid;
+  if (cid > 0) {
+    var cacheId = cid + (isString ? el : getOuterHTML(el));
+    linker = linkerCache.get(cacheId);
+    if (!linker) {
+      linker = compile(template, vm.$options, true);
+      linkerCache.put(cacheId, linker);
+    }
+  } else {
+    linker = compile(template, vm.$options, true);
+  }
+  this.linker = linker;
+}
+
+/**
+ * Create a fragment instance with given host and scope.
+ *
+ * @param {Vue} host
+ * @param {Object} scope
+ * @param {Fragment} parentFrag
+ */
+
+FragmentFactory.prototype.create = function (host, scope, parentFrag) {
+  var frag = cloneNode(this.template);
+  return new Fragment(this.linker, this.vm, frag, host, scope, parentFrag);
+};
+
+var ON = 700;
+var MODEL = 800;
+var BIND = 850;
+var TRANSITION = 1100;
+var EL = 1500;
+var COMPONENT = 1500;
+var PARTIAL = 1750;
+var IF = 2100;
+var FOR = 2200;
+var SLOT = 2300;
+
+var uid$3 = 0;
+
+var vFor = {
+
+  priority: FOR,
+  terminal: true,
+
+  params: ['track-by', 'stagger', 'enter-stagger', 'leave-stagger'],
+
+  bind: function bind() {
+    if ('development' !== 'production' && this.el.hasAttribute('v-if')) {
+      warn('<' + this.el.tagName.toLowerCase() + ' v-for="' + this.expression + '" v-if="' + this.el.getAttribute('v-if') + '">: ' + 'Using v-if and v-for on the same element is not recommended - ' + 'consider filtering the source Array instead.', this.vm);
+    }
+
+    // support "item in/of items" syntax
+    var inMatch = this.expression.match(/(.*) (?:in|of) (.*)/);
+    if (inMatch) {
+      var itMatch = inMatch[1].match(/\((.*),(.*)\)/);
+      if (itMatch) {
+        this.iterator = itMatch[1].trim();
+        this.alias = itMatch[2].trim();
+      } else {
+        this.alias = inMatch[1].trim();
+      }
+      this.expression = inMatch[2];
+    }
+
+    if (!this.alias) {
+      'development' !== 'production' && warn('Invalid v-for expression "' + this.descriptor.raw + '": ' + 'alias is required.', this.vm);
+      return;
+    }
+
+    // uid as a cache identifier
+    this.id = '__v-for__' + ++uid$3;
+
+    // check if this is an option list,
+    // so that we know if we need to update the <select>'s
+    // v-model when the option list has changed.
+    // because v-model has a lower priority than v-for,
+    // the v-model is not bound here yet, so we have to
+    // retrive it in the actual updateModel() function.
+    var tag = this.el.tagName;
+    this.isOption = (tag === 'OPTION' || tag === 'OPTGROUP') && this.el.parentNode.tagName === 'SELECT';
+
+    // setup anchor nodes
+    this.start = createAnchor('v-for-start');
+    this.end = createAnchor('v-for-end');
+    replace(this.el, this.end);
+    before(this.start, this.end);
+
+    // cache
+    this.cache = Object.create(null);
+
+    // fragment factory
+    this.factory = new FragmentFactory(this.vm, this.el);
+  },
+
+  update: function update(data) {
+    this.diff(data);
+    this.updateRef();
+    this.updateModel();
+  },
+
+  /**
+   * Diff, based on new data and old data, determine the
+   * minimum amount of DOM manipulations needed to make the
+   * DOM reflect the new data Array.
+   *
+   * The algorithm diffs the new data Array by storing a
+   * hidden reference to an owner vm instance on previously
+   * seen data. This allows us to achieve O(n) which is
+   * better than a levenshtein distance based algorithm,
+   * which is O(m * n).
+   *
+   * @param {Array} data
+   */
+
+  diff: function diff(data) {
+    // check if the Array was converted from an Object
+    var item = data[0];
+    var convertedFromObject = this.fromObject = isObject(item) && hasOwn(item, '$key') && hasOwn(item, '$value');
+
+    var trackByKey = this.params.trackBy;
+    var oldFrags = this.frags;
+    var frags = this.frags = new Array(data.length);
+    var alias = this.alias;
+    var iterator = this.iterator;
+    var start = this.start;
+    var end = this.end;
+    var inDocument = inDoc(start);
+    var init = !oldFrags;
+    var i, l, frag, key, value, primitive;
+
+    // First pass, go through the new Array and fill up
+    // the new frags array. If a piece of data has a cached
+    // instance for it, we reuse it. Otherwise build a new
+    // instance.
+    for (i = 0, l = data.length; i < l; i++) {
+      item = data[i];
+      key = convertedFromObject ? item.$key : null;
+      value = convertedFromObject ? item.$value : item;
+      primitive = !isObject(value);
+      frag = !init && this.getCachedFrag(value, i, key);
+      if (frag) {
+        // reusable fragment
+        frag.reused = true;
+        // update $index
+        frag.scope.$index = i;
+        // update $key
+        if (key) {
+          frag.scope.$key = key;
+        }
+        // update iterator
+        if (iterator) {
+          frag.scope[iterator] = key !== null ? key : i;
+        }
+        // update data for track-by, object repeat &
+        // primitive values.
+        if (trackByKey || convertedFromObject || primitive) {
+          withoutConversion(function () {
+            frag.scope[alias] = value;
+          });
+        }
+      } else {
+        // new instance
+        frag = this.create(value, alias, i, key);
+        frag.fresh = !init;
+      }
+      frags[i] = frag;
+      if (init) {
+        frag.before(end);
+      }
+    }
+
+    // we're done for the initial render.
+    if (init) {
+      return;
+    }
+
+    // Second pass, go through the old fragments and
+    // destroy those who are not reused (and remove them
+    // from cache)
+    var removalIndex = 0;
+    var totalRemoved = oldFrags.length - frags.length;
+    // when removing a large number of fragments, watcher removal
+    // turns out to be a perf bottleneck, so we batch the watcher
+    // removals into a single filter call!
+    this.vm._vForRemoving = true;
+    for (i = 0, l = oldFrags.length; i < l; i++) {
+      frag = oldFrags[i];
+      if (!frag.reused) {
+        this.deleteCachedFrag(frag);
+        this.remove(frag, removalIndex++, totalRemoved, inDocument);
+      }
+    }
+    this.vm._vForRemoving = false;
+    if (removalIndex) {
+      this.vm._watchers = this.vm._watchers.filter(function (w) {
+        return w.active;
+      });
+    }
+
+    // Final pass, move/insert new fragments into the
+    // right place.
+    var targetPrev, prevEl, currentPrev;
+    var insertionIndex = 0;
+    for (i = 0, l = frags.length; i < l; i++) {
+      frag = frags[i];
+      // this is the frag that we should be after
+      targetPrev = frags[i - 1];
+      prevEl = targetPrev ? targetPrev.staggerCb ? targetPrev.staggerAnchor : targetPrev.end || targetPrev.node : start;
+      if (frag.reused && !frag.staggerCb) {
+        currentPrev = findPrevFrag(frag, start, this.id);
+        if (currentPrev !== targetPrev && (!currentPrev ||
+        // optimization for moving a single item.
+        // thanks to suggestions by @livoras in #1807
+        findPrevFrag(currentPrev, start, this.id) !== targetPrev)) {
+          this.move(frag, prevEl);
+        }
+      } else {
+        // new instance, or still in stagger.
+        // insert with updated stagger index.
+        this.insert(frag, insertionIndex++, prevEl, inDocument);
+      }
+      frag.reused = frag.fresh = false;
+    }
+  },
+
+  /**
+   * Create a new fragment instance.
+   *
+   * @param {*} value
+   * @param {String} alias
+   * @param {Number} index
+   * @param {String} [key]
+   * @return {Fragment}
+   */
+
+  create: function create(value, alias, index, key) {
+    var host = this._host;
+    // create iteration scope
+    var parentScope = this._scope || this.vm;
+    var scope = Object.create(parentScope);
+    // ref holder for the scope
+    scope.$refs = Object.create(parentScope.$refs);
+    scope.$els = Object.create(parentScope.$els);
+    // make sure point $parent to parent scope
+    scope.$parent = parentScope;
+    // for two-way binding on alias
+    scope.$forContext = this;
+    // define scope properties
+    // important: define the scope alias without forced conversion
+    // so that frozen data structures remain non-reactive.
+    withoutConversion(function () {
+      defineReactive(scope, alias, value);
+    });
+    defineReactive(scope, '$index', index);
+    if (key) {
+      defineReactive(scope, '$key', key);
+    } else if (scope.$key) {
+      // avoid accidental fallback
+      def(scope, '$key', null);
+    }
+    if (this.iterator) {
+      defineReactive(scope, this.iterator, key !== null ? key : index);
+    }
+    var frag = this.factory.create(host, scope, this._frag);
+    frag.forId = this.id;
+    this.cacheFrag(value, frag, index, key);
+    return frag;
+  },
+
+  /**
+   * Update the v-ref on owner vm.
+   */
+
+  updateRef: function updateRef() {
+    var ref = this.descriptor.ref;
+    if (!ref) return;
+    var hash = (this._scope || this.vm).$refs;
+    var refs;
+    if (!this.fromObject) {
+      refs = this.frags.map(findVmFromFrag);
+    } else {
+      refs = {};
+      this.frags.forEach(function (frag) {
+        refs[frag.scope.$key] = findVmFromFrag(frag);
+      });
+    }
+    hash[ref] = refs;
+  },
+
+  /**
+   * For option lists, update the containing v-model on
+   * parent <select>.
+   */
+
+  updateModel: function updateModel() {
+    if (this.isOption) {
+      var parent = this.start.parentNode;
+      var model = parent && parent.__v_model;
+      if (model) {
+        model.forceUpdate();
+      }
+    }
+  },
+
+  /**
+   * Insert a fragment. Handles staggering.
+   *
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {Node} prevEl
+   * @param {Boolean} inDocument
+   */
+
+  insert: function insert(frag, index, prevEl, inDocument) {
+    if (frag.staggerCb) {
+      frag.staggerCb.cancel();
+      frag.staggerCb = null;
+    }
+    var staggerAmount = this.getStagger(frag, index, null, 'enter');
+    if (inDocument && staggerAmount) {
+      // create an anchor and insert it synchronously,
+      // so that we can resolve the correct order without
+      // worrying about some elements not inserted yet
+      var anchor = frag.staggerAnchor;
+      if (!anchor) {
+        anchor = frag.staggerAnchor = createAnchor('stagger-anchor');
+        anchor.__v_frag = frag;
+      }
+      after(anchor, prevEl);
+      var op = frag.staggerCb = cancellable(function () {
+        frag.staggerCb = null;
+        frag.before(anchor);
+        remove(anchor);
+      });
+      setTimeout(op, staggerAmount);
+    } else {
+      var target = prevEl.nextSibling;
+      /* istanbul ignore if */
+      if (!target) {
+        // reset end anchor position in case the position was messed up
+        // by an external drag-n-drop library.
+        after(this.end, prevEl);
+        target = this.end;
+      }
+      frag.before(target);
+    }
+  },
+
+  /**
+   * Remove a fragment. Handles staggering.
+   *
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {Number} total
+   * @param {Boolean} inDocument
+   */
+
+  remove: function remove(frag, index, total, inDocument) {
+    if (frag.staggerCb) {
+      frag.staggerCb.cancel();
+      frag.staggerCb = null;
+      // it's not possible for the same frag to be removed
+      // twice, so if we have a pending stagger callback,
+      // it means this frag is queued for enter but removed
+      // before its transition started. Since it is already
+      // destroyed, we can just leave it in detached state.
+      return;
+    }
+    var staggerAmount = this.getStagger(frag, index, total, 'leave');
+    if (inDocument && staggerAmount) {
+      var op = frag.staggerCb = cancellable(function () {
+        frag.staggerCb = null;
+        frag.remove();
+      });
+      setTimeout(op, staggerAmount);
+    } else {
+      frag.remove();
+    }
+  },
+
+  /**
+   * Move a fragment to a new position.
+   * Force no transition.
+   *
+   * @param {Fragment} frag
+   * @param {Node} prevEl
+   */
+
+  move: function move(frag, prevEl) {
+    // fix a common issue with Sortable:
+    // if prevEl doesn't have nextSibling, this means it's
+    // been dragged after the end anchor. Just re-position
+    // the end anchor to the end of the container.
+    /* istanbul ignore if */
+    if (!prevEl.nextSibling) {
+      this.end.parentNode.appendChild(this.end);
+    }
+    frag.before(prevEl.nextSibling, false);
+  },
+
+  /**
+   * Cache a fragment using track-by or the object key.
+   *
+   * @param {*} value
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {String} [key]
+   */
+
+  cacheFrag: function cacheFrag(value, frag, index, key) {
+    var trackByKey = this.params.trackBy;
+    var cache = this.cache;
+    var primitive = !isObject(value);
+    var id;
+    if (key || trackByKey || primitive) {
+      id = getTrackByKey(index, key, value, trackByKey);
+      if (!cache[id]) {
+        cache[id] = frag;
+      } else if (trackByKey !== '$index') {
+        'development' !== 'production' && this.warnDuplicate(value);
+      }
+    } else {
+      id = this.id;
+      if (hasOwn(value, id)) {
+        if (value[id] === null) {
+          value[id] = frag;
+        } else {
+          'development' !== 'production' && this.warnDuplicate(value);
+        }
+      } else if (Object.isExtensible(value)) {
+        def(value, id, frag);
+      } else if ('development' !== 'production') {
+        warn('Frozen v-for objects cannot be automatically tracked, make sure to ' + 'provide a track-by key.');
+      }
+    }
+    frag.raw = value;
+  },
+
+  /**
+   * Get a cached fragment from the value/index/key
+   *
+   * @param {*} value
+   * @param {Number} index
+   * @param {String} key
+   * @return {Fragment}
+   */
+
+  getCachedFrag: function getCachedFrag(value, index, key) {
+    var trackByKey = this.params.trackBy;
+    var primitive = !isObject(value);
+    var frag;
+    if (key || trackByKey || primitive) {
+      var id = getTrackByKey(index, key, value, trackByKey);
+      frag = this.cache[id];
+    } else {
+      frag = value[this.id];
+    }
+    if (frag && (frag.reused || frag.fresh)) {
+      'development' !== 'production' && this.warnDuplicate(value);
+    }
+    return frag;
+  },
+
+  /**
+   * Delete a fragment from cache.
+   *
+   * @param {Fragment} frag
+   */
+
+  deleteCachedFrag: function deleteCachedFrag(frag) {
+    var value = frag.raw;
+    var trackByKey = this.params.trackBy;
+    var scope = frag.scope;
+    var index = scope.$index;
+    // fix #948: avoid accidentally fall through to
+    // a parent repeater which happens to have $key.
+    var key = hasOwn(scope, '$key') && scope.$key;
+    var primitive = !isObject(value);
+    if (trackByKey || key || primitive) {
+      var id = getTrackByKey(index, key, value, trackByKey);
+      this.cache[id] = null;
+    } else {
+      value[this.id] = null;
+      frag.raw = null;
+    }
+  },
+
+  /**
+   * Get the stagger amount for an insertion/removal.
+   *
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {Number} total
+   * @param {String} type
+   */
+
+  getStagger: function getStagger(frag, index, total, type) {
+    type = type + 'Stagger';
+    var trans = frag.node.__v_trans;
+    var hooks = trans && trans.hooks;
+    var hook = hooks && (hooks[type] || hooks.stagger);
+    return hook ? hook.call(frag, index, total) : index * parseInt(this.params[type] || this.params.stagger, 10);
+  },
+
+  /**
+   * Pre-process the value before piping it through the
+   * filters. This is passed to and called by the watcher.
+   */
+
+  _preProcess: function _preProcess(value) {
+    // regardless of type, store the un-filtered raw value.
+    this.rawValue = value;
+    return value;
+  },
+
+  /**
+   * Post-process the value after it has been piped through
+   * the filters. This is passed to and called by the watcher.
+   *
+   * It is necessary for this to be called during the
+   * watcher's dependency collection phase because we want
+   * the v-for to update when the source Object is mutated.
+   */
+
+  _postProcess: function _postProcess(value) {
+    if (isArray(value)) {
+      return value;
+    } else if (isPlainObject(value)) {
+      // convert plain object to array.
+      var keys = Object.keys(value);
+      var i = keys.length;
+      var res = new Array(i);
+      var key;
+      while (i--) {
+        key = keys[i];
+        res[i] = {
+          $key: key,
+          $value: value[key]
+        };
+      }
+      return res;
+    } else {
+      if (typeof value === 'number' && !isNaN(value)) {
+        value = range(value);
+      }
+      return value || [];
+    }
+  },
+
+  unbind: function unbind() {
+    if (this.descriptor.ref) {
+      (this._scope || this.vm).$refs[this.descriptor.ref] = null;
+    }
+    if (this.frags) {
+      var i = this.frags.length;
+      var frag;
+      while (i--) {
+        frag = this.frags[i];
+        this.deleteCachedFrag(frag);
+        frag.destroy();
+      }
+    }
+  }
+};
+
+/**
+ * Helper to find the previous element that is a fragment
+ * anchor. This is necessary because a destroyed frag's
+ * element could still be lingering in the DOM before its
+ * leaving transition finishes, but its inserted flag
+ * should have been set to false so we can skip them.
+ *
+ * If this is a block repeat, we want to make sure we only
+ * return frag that is bound to this v-for. (see #929)
+ *
+ * @param {Fragment} frag
+ * @param {Comment|Text} anchor
+ * @param {String} id
+ * @return {Fragment}
+ */
+
+function findPrevFrag(frag, anchor, id) {
+  var el = frag.node.previousSibling;
+  /* istanbul ignore if */
+  if (!el) return;
+  frag = el.__v_frag;
+  while ((!frag || frag.forId !== id || !frag.inserted) && el !== anchor) {
+    el = el.previousSibling;
+    /* istanbul ignore if */
+    if (!el) return;
+    frag = el.__v_frag;
+  }
+  return frag;
+}
+
+/**
+ * Create a range array from given number.
+ *
+ * @param {Number} n
+ * @return {Array}
+ */
+
+function range(n) {
+  var i = -1;
+  var ret = new Array(Math.floor(n));
+  while (++i < n) {
+    ret[i] = i;
+  }
+  return ret;
+}
+
+/**
+ * Get the track by key for an item.
+ *
+ * @param {Number} index
+ * @param {String} key
+ * @param {*} value
+ * @param {String} [trackByKey]
+ */
+
+function getTrackByKey(index, key, value, trackByKey) {
+  return trackByKey ? trackByKey === '$index' ? index : trackByKey.charAt(0).match(/\w/) ? getPath(value, trackByKey) : value[trackByKey] : key || value;
+}
+
+if ('development' !== 'production') {
+  vFor.warnDuplicate = function (value) {
+    warn('Duplicate value found in v-for="' + this.descriptor.raw + '": ' + JSON.stringify(value) + '. Use track-by="$index" if ' + 'you are expecting duplicate values.', this.vm);
+  };
+}
+
+/**
+ * Find a vm from a fragment.
+ *
+ * @param {Fragment} frag
+ * @return {Vue|undefined}
+ */
+
+function findVmFromFrag(frag) {
+  var node = frag.node;
+  // handle multi-node frag
+  if (frag.end) {
+    while (!node.__vue__ && node !== frag.end && node.nextSibling) {
+      node = node.nextSibling;
+    }
+  }
+  return node.__vue__;
+}
+
+var vIf = {
+
+  priority: IF,
+  terminal: true,
+
+  bind: function bind() {
+    var el = this.el;
+    if (!el.__vue__) {
+      // check else block
+      var next = el.nextElementSibling;
+      if (next && getAttr(next, 'v-else') !== null) {
+        remove(next);
+        this.elseEl = next;
+      }
+      // check main block
+      this.anchor = createAnchor('v-if');
+      replace(el, this.anchor);
+    } else {
+      'development' !== 'production' && warn('v-if="' + this.expression + '" cannot be ' + 'used on an instance root element.', this.vm);
+      this.invalid = true;
+    }
+  },
+
+  update: function update(value) {
+    if (this.invalid) return;
+    if (value) {
+      if (!this.frag) {
+        this.insert();
+      }
+    } else {
+      this.remove();
+    }
+  },
+
+  insert: function insert() {
+    if (this.elseFrag) {
+      this.elseFrag.remove();
+      this.elseFrag = null;
+    }
+    // lazy init factory
+    if (!this.factory) {
+      this.factory = new FragmentFactory(this.vm, this.el);
+    }
+    this.frag = this.factory.create(this._host, this._scope, this._frag);
+    this.frag.before(this.anchor);
+  },
+
+  remove: function remove() {
+    if (this.frag) {
+      this.frag.remove();
+      this.frag = null;
+    }
+    if (this.elseEl && !this.elseFrag) {
+      if (!this.elseFactory) {
+        this.elseFactory = new FragmentFactory(this.elseEl._context || this.vm, this.elseEl);
+      }
+      this.elseFrag = this.elseFactory.create(this._host, this._scope, this._frag);
+      this.elseFrag.before(this.anchor);
+    }
+  },
+
+  unbind: function unbind() {
+    if (this.frag) {
+      this.frag.destroy();
+    }
+    if (this.elseFrag) {
+      this.elseFrag.destroy();
+    }
+  }
+};
+
+var show = {
+
+  bind: function bind() {
+    // check else block
+    var next = this.el.nextElementSibling;
+    if (next && getAttr(next, 'v-else') !== null) {
+      this.elseEl = next;
+    }
+  },
+
+  update: function update(value) {
+    this.apply(this.el, value);
+    if (this.elseEl) {
+      this.apply(this.elseEl, !value);
+    }
+  },
+
+  apply: function apply(el, value) {
+    if (inDoc(el)) {
+      applyTransition(el, value ? 1 : -1, toggle, this.vm);
+    } else {
+      toggle();
+    }
+    function toggle() {
+      el.style.display = value ? '' : 'none';
+    }
+  }
+};
+
+var text$2 = {
+
+  bind: function bind() {
+    var self = this;
+    var el = this.el;
+    var isRange = el.type === 'range';
+    var lazy = this.params.lazy;
+    var number = this.params.number;
+    var debounce = this.params.debounce;
+
+    // handle composition events.
+    //   http://blog.evanyou.me/2014/01/03/composition-event/
+    // skip this for Android because it handles composition
+    // events quite differently. Android doesn't trigger
+    // composition events for language input methods e.g.
+    // Chinese, but instead triggers them for spelling
+    // suggestions... (see Discussion/#162)
+    var composing = false;
+    if (!isAndroid && !isRange) {
+      this.on('compositionstart', function () {
+        composing = true;
+      });
+      this.on('compositionend', function () {
+        composing = false;
+        // in IE11 the "compositionend" event fires AFTER
+        // the "input" event, so the input handler is blocked
+        // at the end... have to call it here.
+        //
+        // #1327: in lazy mode this is unecessary.
+        if (!lazy) {
+          self.listener();
+        }
+      });
+    }
+
+    // prevent messing with the input when user is typing,
+    // and force update on blur.
+    this.focused = false;
+    if (!isRange && !lazy) {
+      this.on('focus', function () {
+        self.focused = true;
+      });
+      this.on('blur', function () {
+        self.focused = false;
+        // do not sync value after fragment removal (#2017)
+        if (!self._frag || self._frag.inserted) {
+          self.rawListener();
+        }
+      });
+    }
+
+    // Now attach the main listener
+    this.listener = this.rawListener = function () {
+      if (composing || !self._bound) {
+        return;
+      }
+      var val = number || isRange ? toNumber(el.value) : el.value;
+      self.set(val);
+      // force update on next tick to avoid lock & same value
+      // also only update when user is not typing
+      nextTick(function () {
+        if (self._bound && !self.focused) {
+          self.update(self._watcher.value);
+        }
+      });
+    };
+
+    // apply debounce
+    if (debounce) {
+      this.listener = _debounce(this.listener, debounce);
+    }
+
+    // Support jQuery events, since jQuery.trigger() doesn't
+    // trigger native events in some cases and some plugins
+    // rely on $.trigger()
+    //
+    // We want to make sure if a listener is attached using
+    // jQuery, it is also removed with jQuery, that's why
+    // we do the check for each directive instance and
+    // store that check result on itself. This also allows
+    // easier test coverage control by unsetting the global
+    // jQuery variable in tests.
+    this.hasjQuery = typeof jQuery === 'function';
+    if (this.hasjQuery) {
+      var method = jQuery.fn.on ? 'on' : 'bind';
+      jQuery(el)[method]('change', this.rawListener);
+      if (!lazy) {
+        jQuery(el)[method]('input', this.listener);
+      }
+    } else {
+      this.on('change', this.rawListener);
+      if (!lazy) {
+        this.on('input', this.listener);
+      }
+    }
+
+    // IE9 doesn't fire input event on backspace/del/cut
+    if (!lazy && isIE9) {
+      this.on('cut', function () {
+        nextTick(self.listener);
+      });
+      this.on('keyup', function (e) {
+        if (e.keyCode === 46 || e.keyCode === 8) {
+          self.listener();
+        }
+      });
+    }
+
+    // set initial value if present
+    if (el.hasAttribute('value') || el.tagName === 'TEXTAREA' && el.value.trim()) {
+      this.afterBind = this.listener;
+    }
+  },
+
+  update: function update(value) {
+    // #3029 only update when the value changes. This prevent
+    // browsers from overwriting values like selectionStart
+    value = _toString(value);
+    if (value !== this.el.value) this.el.value = value;
+  },
+
+  unbind: function unbind() {
+    var el = this.el;
+    if (this.hasjQuery) {
+      var method = jQuery.fn.off ? 'off' : 'unbind';
+      jQuery(el)[method]('change', this.listener);
+      jQuery(el)[method]('input', this.listener);
+    }
+  }
+};
+
+var radio = {
+
+  bind: function bind() {
+    var self = this;
+    var el = this.el;
+
+    this.getValue = function () {
+      // value overwrite via v-bind:value
+      if (el.hasOwnProperty('_value')) {
+        return el._value;
+      }
+      var val = el.value;
+      if (self.params.number) {
+        val = toNumber(val);
+      }
+      return val;
+    };
+
+    this.listener = function () {
+      self.set(self.getValue());
+    };
+    this.on('change', this.listener);
+
+    if (el.hasAttribute('checked')) {
+      this.afterBind = this.listener;
+    }
+  },
+
+  update: function update(value) {
+    this.el.checked = looseEqual(value, this.getValue());
+  }
+};
+
+var select = {
+
+  bind: function bind() {
+    var _this = this;
+
+    var self = this;
+    var el = this.el;
+
+    // method to force update DOM using latest value.
+    this.forceUpdate = function () {
+      if (self._watcher) {
+        self.update(self._watcher.get());
+      }
+    };
+
+    // check if this is a multiple select
+    var multiple = this.multiple = el.hasAttribute('multiple');
+
+    // attach listener
+    this.listener = function () {
+      var value = getValue(el, multiple);
+      value = self.params.number ? isArray(value) ? value.map(toNumber) : toNumber(value) : value;
+      self.set(value);
+    };
+    this.on('change', this.listener);
+
+    // if has initial value, set afterBind
+    var initValue = getValue(el, multiple, true);
+    if (multiple && initValue.length || !multiple && initValue !== null) {
+      this.afterBind = this.listener;
+    }
+
+    // All major browsers except Firefox resets
+    // selectedIndex with value -1 to 0 when the element
+    // is appended to a new parent, therefore we have to
+    // force a DOM update whenever that happens...
+    this.vm.$on('hook:attached', function () {
+      nextTick(_this.forceUpdate);
+    });
+    if (!inDoc(el)) {
+      nextTick(this.forceUpdate);
+    }
+  },
+
+  update: function update(value) {
+    var el = this.el;
+    el.selectedIndex = -1;
+    var multi = this.multiple && isArray(value);
+    var options = el.options;
+    var i = options.length;
+    var op, val;
+    while (i--) {
+      op = options[i];
+      val = op.hasOwnProperty('_value') ? op._value : op.value;
+      /* eslint-disable eqeqeq */
+      op.selected = multi ? indexOf$1(value, val) > -1 : looseEqual(value, val);
+      /* eslint-enable eqeqeq */
+    }
+  },
+
+  unbind: function unbind() {
+    /* istanbul ignore next */
+    this.vm.$off('hook:attached', this.forceUpdate);
+  }
+};
+
+/**
+ * Get select value
+ *
+ * @param {SelectElement} el
+ * @param {Boolean} multi
+ * @param {Boolean} init
+ * @return {Array|*}
+ */
+
+function getValue(el, multi, init) {
+  var res = multi ? [] : null;
+  var op, val, selected;
+  for (var i = 0, l = el.options.length; i < l; i++) {
+    op = el.options[i];
+    selected = init ? op.hasAttribute('selected') : op.selected;
+    if (selected) {
+      val = op.hasOwnProperty('_value') ? op._value : op.value;
+      if (multi) {
+        res.push(val);
+      } else {
+        return val;
+      }
+    }
+  }
+  return res;
+}
+
+/**
+ * Native Array.indexOf uses strict equal, but in this
+ * case we need to match string/numbers with custom equal.
+ *
+ * @param {Array} arr
+ * @param {*} val
+ */
+
+function indexOf$1(arr, val) {
+  var i = arr.length;
+  while (i--) {
+    if (looseEqual(arr[i], val)) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+var checkbox = {
+
+  bind: function bind() {
+    var self = this;
+    var el = this.el;
+
+    this.getValue = function () {
+      return el.hasOwnProperty('_value') ? el._value : self.params.number ? toNumber(el.value) : el.value;
+    };
+
+    function getBooleanValue() {
+      var val = el.checked;
+      if (val && el.hasOwnProperty('_trueValue')) {
+        return el._trueValue;
+      }
+      if (!val && el.hasOwnProperty('_falseValue')) {
+        return el._falseValue;
+      }
+      return val;
+    }
+
+    this.listener = function () {
+      var model = self._watcher.get();
+      if (isArray(model)) {
+        var val = self.getValue();
+        var i = indexOf(model, val);
+        if (el.checked) {
+          if (i < 0) {
+            self.set(model.concat(val));
+          }
+        } else if (i > -1) {
+          self.set(model.slice(0, i).concat(model.slice(i + 1)));
+        }
+      } else {
+        self.set(getBooleanValue());
+      }
+    };
+
+    this.on('change', this.listener);
+    if (el.hasAttribute('checked')) {
+      this.afterBind = this.listener;
+    }
+  },
+
+  update: function update(value) {
+    var el = this.el;
+    if (isArray(value)) {
+      el.checked = indexOf(value, this.getValue()) > -1;
+    } else {
+      if (el.hasOwnProperty('_trueValue')) {
+        el.checked = looseEqual(value, el._trueValue);
+      } else {
+        el.checked = !!value;
+      }
+    }
+  }
+};
+
+var handlers = {
+  text: text$2,
+  radio: radio,
+  select: select,
+  checkbox: checkbox
+};
+
+var model = {
+
+  priority: MODEL,
+  twoWay: true,
+  handlers: handlers,
+  params: ['lazy', 'number', 'debounce'],
+
+  /**
+   * Possible elements:
+   *   <select>
+   *   <textarea>
+   *   <input type="*">
+   *     - text
+   *     - checkbox
+   *     - radio
+   *     - number
+   */
+
+  bind: function bind() {
+    // friendly warning...
+    this.checkFilters();
+    if (this.hasRead && !this.hasWrite) {
+      'development' !== 'production' && warn('It seems you are using a read-only filter with ' + 'v-model="' + this.descriptor.raw + '". ' + 'You might want to use a two-way filter to ensure correct behavior.', this.vm);
+    }
+    var el = this.el;
+    var tag = el.tagName;
+    var handler;
+    if (tag === 'INPUT') {
+      handler = handlers[el.type] || handlers.text;
+    } else if (tag === 'SELECT') {
+      handler = handlers.select;
+    } else if (tag === 'TEXTAREA') {
+      handler = handlers.text;
+    } else {
+      'development' !== 'production' && warn('v-model does not support element type: ' + tag, this.vm);
+      return;
+    }
+    el.__v_model = this;
+    handler.bind.call(this);
+    this.update = handler.update;
+    this._unbind = handler.unbind;
+  },
+
+  /**
+   * Check read/write filter stats.
+   */
+
+  checkFilters: function checkFilters() {
+    var filters = this.filters;
+    if (!filters) return;
+    var i = filters.length;
+    while (i--) {
+      var filter = resolveAsset(this.vm.$options, 'filters', filters[i].name);
+      if (typeof filter === 'function' || filter.read) {
+        this.hasRead = true;
+      }
+      if (filter.write) {
+        this.hasWrite = true;
+      }
+    }
+  },
+
+  unbind: function unbind() {
+    this.el.__v_model = null;
+    this._unbind && this._unbind();
+  }
+};
+
+// keyCode aliases
+var keyCodes = {
+  esc: 27,
+  tab: 9,
+  enter: 13,
+  space: 32,
+  'delete': [8, 46],
+  up: 38,
+  left: 37,
+  right: 39,
+  down: 40
+};
+
+function keyFilter(handler, keys) {
+  var codes = keys.map(function (key) {
+    var charCode = key.charCodeAt(0);
+    if (charCode > 47 && charCode < 58) {
+      return parseInt(key, 10);
+    }
+    if (key.length === 1) {
+      charCode = key.toUpperCase().charCodeAt(0);
+      if (charCode > 64 && charCode < 91) {
+        return charCode;
+      }
+    }
+    return keyCodes[key];
+  });
+  codes = [].concat.apply([], codes);
+  return function keyHandler(e) {
+    if (codes.indexOf(e.keyCode) > -1) {
+      return handler.call(this, e);
+    }
+  };
+}
+
+function stopFilter(handler) {
+  return function stopHandler(e) {
+    e.stopPropagation();
+    return handler.call(this, e);
+  };
+}
+
+function preventFilter(handler) {
+  return function preventHandler(e) {
+    e.preventDefault();
+    return handler.call(this, e);
+  };
+}
+
+function selfFilter(handler) {
+  return function selfHandler(e) {
+    if (e.target === e.currentTarget) {
+      return handler.call(this, e);
+    }
+  };
+}
+
+var on$1 = {
+
+  priority: ON,
+  acceptStatement: true,
+  keyCodes: keyCodes,
+
+  bind: function bind() {
+    // deal with iframes
+    if (this.el.tagName === 'IFRAME' && this.arg !== 'load') {
+      var self = this;
+      this.iframeBind = function () {
+        on(self.el.contentWindow, self.arg, self.handler, self.modifiers.capture);
+      };
+      this.on('load', this.iframeBind);
+    }
+  },
+
+  update: function update(handler) {
+    // stub a noop for v-on with no value,
+    // e.g. @mousedown.prevent
+    if (!this.descriptor.raw) {
+      handler = function () {};
+    }
+
+    if (typeof handler !== 'function') {
+      'development' !== 'production' && warn('v-on:' + this.arg + '="' + this.expression + '" expects a function value, ' + 'got ' + handler, this.vm);
+      return;
+    }
+
+    // apply modifiers
+    if (this.modifiers.stop) {
+      handler = stopFilter(handler);
+    }
+    if (this.modifiers.prevent) {
+      handler = preventFilter(handler);
+    }
+    if (this.modifiers.self) {
+      handler = selfFilter(handler);
+    }
+    // key filter
+    var keys = Object.keys(this.modifiers).filter(function (key) {
+      return key !== 'stop' && key !== 'prevent' && key !== 'self' && key !== 'capture';
+    });
+    if (keys.length) {
+      handler = keyFilter(handler, keys);
+    }
+
+    this.reset();
+    this.handler = handler;
+
+    if (this.iframeBind) {
+      this.iframeBind();
+    } else {
+      on(this.el, this.arg, this.handler, this.modifiers.capture);
+    }
+  },
+
+  reset: function reset() {
+    var el = this.iframeBind ? this.el.contentWindow : this.el;
+    if (this.handler) {
+      off(el, this.arg, this.handler);
+    }
+  },
+
+  unbind: function unbind() {
+    this.reset();
+  }
+};
+
+var prefixes = ['-webkit-', '-moz-', '-ms-'];
+var camelPrefixes = ['Webkit', 'Moz', 'ms'];
+var importantRE = /!important;?$/;
+var propCache = Object.create(null);
+
+var testEl = null;
+
+var style = {
+
+  deep: true,
+
+  update: function update(value) {
+    if (typeof value === 'string') {
+      this.el.style.cssText = value;
+    } else if (isArray(value)) {
+      this.handleObject(value.reduce(extend, {}));
+    } else {
+      this.handleObject(value || {});
+    }
+  },
+
+  handleObject: function handleObject(value) {
+    // cache object styles so that only changed props
+    // are actually updated.
+    var cache = this.cache || (this.cache = {});
+    var name, val;
+    for (name in cache) {
+      if (!(name in value)) {
+        this.handleSingle(name, null);
+        delete cache[name];
+      }
+    }
+    for (name in value) {
+      val = value[name];
+      if (val !== cache[name]) {
+        cache[name] = val;
+        this.handleSingle(name, val);
+      }
+    }
+  },
+
+  handleSingle: function handleSingle(prop, value) {
+    prop = normalize(prop);
+    if (!prop) return; // unsupported prop
+    // cast possible numbers/booleans into strings
+    if (value != null) value += '';
+    if (value) {
+      var isImportant = importantRE.test(value) ? 'important' : '';
+      if (isImportant) {
+        /* istanbul ignore if */
+        if ('development' !== 'production') {
+          warn('It\'s probably a bad idea to use !important with inline rules. ' + 'This feature will be deprecated in a future version of Vue.');
+        }
+        value = value.replace(importantRE, '').trim();
+        this.el.style.setProperty(prop.kebab, value, isImportant);
+      } else {
+        this.el.style[prop.camel] = value;
+      }
+    } else {
+      this.el.style[prop.camel] = '';
+    }
+  }
+
+};
+
+/**
+ * Normalize a CSS property name.
+ * - cache result
+ * - auto prefix
+ * - camelCase -> dash-case
+ *
+ * @param {String} prop
+ * @return {String}
+ */
+
+function normalize(prop) {
+  if (propCache[prop]) {
+    return propCache[prop];
+  }
+  var res = prefix(prop);
+  propCache[prop] = propCache[res] = res;
+  return res;
+}
+
+/**
+ * Auto detect the appropriate prefix for a CSS property.
+ * https://gist.github.com/paulirish/523692
+ *
+ * @param {String} prop
+ * @return {String}
+ */
+
+function prefix(prop) {
+  prop = hyphenate(prop);
+  var camel = camelize(prop);
+  var upper = camel.charAt(0).toUpperCase() + camel.slice(1);
+  if (!testEl) {
+    testEl = document.createElement('div');
+  }
+  var i = prefixes.length;
+  var prefixed;
+  if (camel !== 'filter' && camel in testEl.style) {
+    return {
+      kebab: prop,
+      camel: camel
+    };
+  }
+  while (i--) {
+    prefixed = camelPrefixes[i] + upper;
+    if (prefixed in testEl.style) {
+      return {
+        kebab: prefixes[i] + prop,
+        camel: prefixed
+      };
+    }
+  }
+}
+
+// xlink
+var xlinkNS = 'http://www.w3.org/1999/xlink';
+var xlinkRE = /^xlink:/;
+
+// check for attributes that prohibit interpolations
+var disallowedInterpAttrRE = /^v-|^:|^@|^(?:is|transition|transition-mode|debounce|track-by|stagger|enter-stagger|leave-stagger)$/;
+// these attributes should also set their corresponding properties
+// because they only affect the initial state of the element
+var attrWithPropsRE = /^(?:value|checked|selected|muted)$/;
+// these attributes expect enumrated values of "true" or "false"
+// but are not boolean attributes
+var enumeratedAttrRE = /^(?:draggable|contenteditable|spellcheck)$/;
+
+// these attributes should set a hidden property for
+// binding v-model to object values
+var modelProps = {
+  value: '_value',
+  'true-value': '_trueValue',
+  'false-value': '_falseValue'
+};
+
+var bind$1 = {
+
+  priority: BIND,
+
+  bind: function bind() {
+    var attr = this.arg;
+    var tag = this.el.tagName;
+    // should be deep watch on object mode
+    if (!attr) {
+      this.deep = true;
+    }
+    // handle interpolation bindings
+    var descriptor = this.descriptor;
+    var tokens = descriptor.interp;
+    if (tokens) {
+      // handle interpolations with one-time tokens
+      if (descriptor.hasOneTime) {
+        this.expression = tokensToExp(tokens, this._scope || this.vm);
+      }
+
+      // only allow binding on native attributes
+      if (disallowedInterpAttrRE.test(attr) || attr === 'name' && (tag === 'PARTIAL' || tag === 'SLOT')) {
+        'development' !== 'production' && warn(attr + '="' + descriptor.raw + '": ' + 'attribute interpolation is not allowed in Vue.js ' + 'directives and special attributes.', this.vm);
+        this.el.removeAttribute(attr);
+        this.invalid = true;
+      }
+
+      /* istanbul ignore if */
+      if ('development' !== 'production') {
+        var raw = attr + '="' + descriptor.raw + '": ';
+        // warn src
+        if (attr === 'src') {
+          warn(raw + 'interpolation in "src" attribute will cause ' + 'a 404 request. Use v-bind:src instead.', this.vm);
+        }
+
+        // warn style
+        if (attr === 'style') {
+          warn(raw + 'interpolation in "style" attribute will cause ' + 'the attribute to be discarded in Internet Explorer. ' + 'Use v-bind:style instead.', this.vm);
+        }
+      }
+    }
+  },
+
+  update: function update(value) {
+    if (this.invalid) {
+      return;
+    }
+    var attr = this.arg;
+    if (this.arg) {
+      this.handleSingle(attr, value);
+    } else {
+      this.handleObject(value || {});
+    }
+  },
+
+  // share object handler with v-bind:class
+  handleObject: style.handleObject,
+
+  handleSingle: function handleSingle(attr, value) {
+    var el = this.el;
+    var interp = this.descriptor.interp;
+    if (this.modifiers.camel) {
+      attr = camelize(attr);
+    }
+    if (!interp && attrWithPropsRE.test(attr) && attr in el) {
+      var attrValue = attr === 'value' ? value == null // IE9 will set input.value to "null" for null...
+      ? '' : value : value;
+
+      if (el[attr] !== attrValue) {
+        el[attr] = attrValue;
+      }
+    }
+    // set model props
+    var modelProp = modelProps[attr];
+    if (!interp && modelProp) {
+      el[modelProp] = value;
+      // update v-model if present
+      var model = el.__v_model;
+      if (model) {
+        model.listener();
+      }
+    }
+    // do not set value attribute for textarea
+    if (attr === 'value' && el.tagName === 'TEXTAREA') {
+      el.removeAttribute(attr);
+      return;
+    }
+    // update attribute
+    if (enumeratedAttrRE.test(attr)) {
+      el.setAttribute(attr, value ? 'true' : 'false');
+    } else if (value != null && value !== false) {
+      if (attr === 'class') {
+        // handle edge case #1960:
+        // class interpolation should not overwrite Vue transition class
+        if (el.__v_trans) {
+          value += ' ' + el.__v_trans.id + '-transition';
+        }
+        setClass(el, value);
+      } else if (xlinkRE.test(attr)) {
+        el.setAttributeNS(xlinkNS, attr, value === true ? '' : value);
+      } else {
+        el.setAttribute(attr, value === true ? '' : value);
+      }
+    } else {
+      el.removeAttribute(attr);
+    }
+  }
+};
+
+var el = {
+
+  priority: EL,
+
+  bind: function bind() {
+    /* istanbul ignore if */
+    if (!this.arg) {
+      return;
+    }
+    var id = this.id = camelize(this.arg);
+    var refs = (this._scope || this.vm).$els;
+    if (hasOwn(refs, id)) {
+      refs[id] = this.el;
+    } else {
+      defineReactive(refs, id, this.el);
+    }
+  },
+
+  unbind: function unbind() {
+    var refs = (this._scope || this.vm).$els;
+    if (refs[this.id] === this.el) {
+      refs[this.id] = null;
+    }
+  }
+};
+
+var ref = {
+  bind: function bind() {
+    'development' !== 'production' && warn('v-ref:' + this.arg + ' must be used on a child ' + 'component. Found on <' + this.el.tagName.toLowerCase() + '>.', this.vm);
+  }
+};
+
+var cloak = {
+  bind: function bind() {
+    var el = this.el;
+    this.vm.$once('pre-hook:compiled', function () {
+      el.removeAttribute('v-cloak');
+    });
+  }
+};
+
+// logic control
+// two-way binding
+// event handling
+// attributes
+// ref & el
+// cloak
+// must export plain object
+var directives = {
+  text: text$1,
+  html: html,
+  'for': vFor,
+  'if': vIf,
+  show: show,
+  model: model,
+  on: on$1,
+  bind: bind$1,
+  el: el,
+  ref: ref,
+  cloak: cloak
+};
+
+var vClass = {
+
+  deep: true,
+
+  update: function update(value) {
+    if (!value) {
+      this.cleanup();
+    } else if (typeof value === 'string') {
+      this.setClass(value.trim().split(/\s+/));
+    } else {
+      this.setClass(normalize$1(value));
+    }
+  },
+
+  setClass: function setClass(value) {
+    this.cleanup(value);
+    for (var i = 0, l = value.length; i < l; i++) {
+      var val = value[i];
+      if (val) {
+        apply(this.el, val, addClass);
+      }
+    }
+    this.prevKeys = value;
+  },
+
+  cleanup: function cleanup(value) {
+    var prevKeys = this.prevKeys;
+    if (!prevKeys) return;
+    var i = prevKeys.length;
+    while (i--) {
+      var key = prevKeys[i];
+      if (!value || value.indexOf(key) < 0) {
+        apply(this.el, key, removeClass);
+      }
+    }
+  }
+};
+
+/**
+ * Normalize objects and arrays (potentially containing objects)
+ * into array of strings.
+ *
+ * @param {Object|Array<String|Object>} value
+ * @return {Array<String>}
+ */
+
+function normalize$1(value) {
+  var res = [];
+  if (isArray(value)) {
+    for (var i = 0, l = value.length; i < l; i++) {
+      var _key = value[i];
+      if (_key) {
+        if (typeof _key === 'string') {
+          res.push(_key);
+        } else {
+          for (var k in _key) {
+            if (_key[k]) res.push(k);
+          }
+        }
+      }
+    }
+  } else if (isObject(value)) {
+    for (var key in value) {
+      if (value[key]) res.push(key);
+    }
+  }
+  return res;
+}
+
+/**
+ * Add or remove a class/classes on an element
+ *
+ * @param {Element} el
+ * @param {String} key The class name. This may or may not
+ *                     contain a space character, in such a
+ *                     case we'll deal with multiple class
+ *                     names at once.
+ * @param {Function} fn
+ */
+
+function apply(el, key, fn) {
+  key = key.trim();
+  if (key.indexOf(' ') === -1) {
+    fn(el, key);
+    return;
+  }
+  // The key contains one or more space characters.
+  // Since a class name doesn't accept such characters, we
+  // treat it as multiple classes.
+  var keys = key.split(/\s+/);
+  for (var i = 0, l = keys.length; i < l; i++) {
+    fn(el, keys[i]);
+  }
+}
+
+var component = {
+
+  priority: COMPONENT,
+
+  params: ['keep-alive', 'transition-mode', 'inline-template'],
+
+  /**
+   * Setup. Two possible usages:
+   *
+   * - static:
+   *   <comp> or <div v-component="comp">
+   *
+   * - dynamic:
+   *   <component :is="view">
+   */
+
+  bind: function bind() {
+    if (!this.el.__vue__) {
+      // keep-alive cache
+      this.keepAlive = this.params.keepAlive;
+      if (this.keepAlive) {
+        this.cache = {};
+      }
+      // check inline-template
+      if (this.params.inlineTemplate) {
+        // extract inline template as a DocumentFragment
+        this.inlineTemplate = extractContent(this.el, true);
+      }
+      // component resolution related state
+      this.pendingComponentCb = this.Component = null;
+      // transition related state
+      this.pendingRemovals = 0;
+      this.pendingRemovalCb = null;
+      // create a ref anchor
+      this.anchor = createAnchor('v-component');
+      replace(this.el, this.anchor);
+      // remove is attribute.
+      // this is removed during compilation, but because compilation is
+      // cached, when the component is used elsewhere this attribute
+      // will remain at link time.
+      this.el.removeAttribute('is');
+      this.el.removeAttribute(':is');
+      // remove ref, same as above
+      if (this.descriptor.ref) {
+        this.el.removeAttribute('v-ref:' + hyphenate(this.descriptor.ref));
+      }
+      // if static, build right now.
+      if (this.literal) {
+        this.setComponent(this.expression);
+      }
+    } else {
+      'development' !== 'production' && warn('cannot mount component "' + this.expression + '" ' + 'on already mounted element: ' + this.el);
+    }
+  },
+
+  /**
+   * Public update, called by the watcher in the dynamic
+   * literal scenario, e.g. <component :is="view">
+   */
+
+  update: function update(value) {
+    if (!this.literal) {
+      this.setComponent(value);
+    }
+  },
+
+  /**
+   * Switch dynamic components. May resolve the component
+   * asynchronously, and perform transition based on
+   * specified transition mode. Accepts a few additional
+   * arguments specifically for vue-router.
+   *
+   * The callback is called when the full transition is
+   * finished.
+   *
+   * @param {String} value
+   * @param {Function} [cb]
+   */
+
+  setComponent: function setComponent(value, cb) {
+    this.invalidatePending();
+    if (!value) {
+      // just remove current
+      this.unbuild(true);
+      this.remove(this.childVM, cb);
+      this.childVM = null;
+    } else {
+      var self = this;
+      this.resolveComponent(value, function () {
+        self.mountComponent(cb);
+      });
+    }
+  },
+
+  /**
+   * Resolve the component constructor to use when creating
+   * the child vm.
+   *
+   * @param {String|Function} value
+   * @param {Function} cb
+   */
+
+  resolveComponent: function resolveComponent(value, cb) {
+    var self = this;
+    this.pendingComponentCb = cancellable(function (Component) {
+      self.ComponentName = Component.options.name || (typeof value === 'string' ? value : null);
+      self.Component = Component;
+      cb();
+    });
+    this.vm._resolveComponent(value, this.pendingComponentCb);
+  },
+
+  /**
+   * Create a new instance using the current constructor and
+   * replace the existing instance. This method doesn't care
+   * whether the new component and the old one are actually
+   * the same.
+   *
+   * @param {Function} [cb]
+   */
+
+  mountComponent: function mountComponent(cb) {
+    // actual mount
+    this.unbuild(true);
+    var self = this;
+    var activateHooks = this.Component.options.activate;
+    var cached = this.getCached();
+    var newComponent = this.build();
+    if (activateHooks && !cached) {
+      this.waitingFor = newComponent;
+      callActivateHooks(activateHooks, newComponent, function () {
+        if (self.waitingFor !== newComponent) {
+          return;
+        }
+        self.waitingFor = null;
+        self.transition(newComponent, cb);
+      });
+    } else {
+      // update ref for kept-alive component
+      if (cached) {
+        newComponent._updateRef();
+      }
+      this.transition(newComponent, cb);
+    }
+  },
+
+  /**
+   * When the component changes or unbinds before an async
+   * constructor is resolved, we need to invalidate its
+   * pending callback.
+   */
+
+  invalidatePending: function invalidatePending() {
+    if (this.pendingComponentCb) {
+      this.pendingComponentCb.cancel();
+      this.pendingComponentCb = null;
+    }
+  },
+
+  /**
+   * Instantiate/insert a new child vm.
+   * If keep alive and has cached instance, insert that
+   * instance; otherwise build a new one and cache it.
+   *
+   * @param {Object} [extraOptions]
+   * @return {Vue} - the created instance
+   */
+
+  build: function build(extraOptions) {
+    var cached = this.getCached();
+    if (cached) {
+      return cached;
+    }
+    if (this.Component) {
+      // default options
+      var options = {
+        name: this.ComponentName,
+        el: cloneNode(this.el),
+        template: this.inlineTemplate,
+        // make sure to add the child with correct parent
+        // if this is a transcluded component, its parent
+        // should be the transclusion host.
+        parent: this._host || this.vm,
+        // if no inline-template, then the compiled
+        // linker can be cached for better performance.
+        _linkerCachable: !this.inlineTemplate,
+        _ref: this.descriptor.ref,
+        _asComponent: true,
+        _isRouterView: this._isRouterView,
+        // if this is a transcluded component, context
+        // will be the common parent vm of this instance
+        // and its host.
+        _context: this.vm,
+        // if this is inside an inline v-for, the scope
+        // will be the intermediate scope created for this
+        // repeat fragment. this is used for linking props
+        // and container directives.
+        _scope: this._scope,
+        // pass in the owner fragment of this component.
+        // this is necessary so that the fragment can keep
+        // track of its contained components in order to
+        // call attach/detach hooks for them.
+        _frag: this._frag
+      };
+      // extra options
+      // in 1.0.0 this is used by vue-router only
+      /* istanbul ignore if */
+      if (extraOptions) {
+        extend(options, extraOptions);
+      }
+      var child = new this.Component(options);
+      if (this.keepAlive) {
+        this.cache[this.Component.cid] = child;
+      }
+      /* istanbul ignore if */
+      if ('development' !== 'production' && this.el.hasAttribute('transition') && child._isFragment) {
+        warn('Transitions will not work on a fragment instance. ' + 'Template: ' + child.$options.template, child);
+      }
+      return child;
+    }
+  },
+
+  /**
+   * Try to get a cached instance of the current component.
+   *
+   * @return {Vue|undefined}
+   */
+
+  getCached: function getCached() {
+    return this.keepAlive && this.cache[this.Component.cid];
+  },
+
+  /**
+   * Teardown the current child, but defers cleanup so
+   * that we can separate the destroy and removal steps.
+   *
+   * @param {Boolean} defer
+   */
+
+  unbuild: function unbuild(defer) {
+    if (this.waitingFor) {
+      if (!this.keepAlive) {
+        this.waitingFor.$destroy();
+      }
+      this.waitingFor = null;
+    }
+    var child = this.childVM;
+    if (!child || this.keepAlive) {
+      if (child) {
+        // remove ref
+        child._inactive = true;
+        child._updateRef(true);
+      }
+      return;
+    }
+    // the sole purpose of `deferCleanup` is so that we can
+    // "deactivate" the vm right now and perform DOM removal
+    // later.
+    child.$destroy(false, defer);
+  },
+
+  /**
+   * Remove current destroyed child and manually do
+   * the cleanup after removal.
+   *
+   * @param {Function} cb
+   */
+
+  remove: function remove(child, cb) {
+    var keepAlive = this.keepAlive;
+    if (child) {
+      // we may have a component switch when a previous
+      // component is still being transitioned out.
+      // we want to trigger only one lastest insertion cb
+      // when the existing transition finishes. (#1119)
+      this.pendingRemovals++;
+      this.pendingRemovalCb = cb;
+      var self = this;
+      child.$remove(function () {
+        self.pendingRemovals--;
+        if (!keepAlive) child._cleanup();
+        if (!self.pendingRemovals && self.pendingRemovalCb) {
+          self.pendingRemovalCb();
+          self.pendingRemovalCb = null;
+        }
+      });
+    } else if (cb) {
+      cb();
+    }
+  },
+
+  /**
+   * Actually swap the components, depending on the
+   * transition mode. Defaults to simultaneous.
+   *
+   * @param {Vue} target
+   * @param {Function} [cb]
+   */
+
+  transition: function transition(target, cb) {
+    var self = this;
+    var current = this.childVM;
+    // for devtool inspection
+    if (current) current._inactive = true;
+    target._inactive = false;
+    this.childVM = target;
+    switch (self.params.transitionMode) {
+      case 'in-out':
+        target.$before(self.anchor, function () {
+          self.remove(current, cb);
+        });
+        break;
+      case 'out-in':
+        self.remove(current, function () {
+          target.$before(self.anchor, cb);
+        });
+        break;
+      default:
+        self.remove(current);
+        target.$before(self.anchor, cb);
+    }
+  },
+
+  /**
+   * Unbind.
+   */
+
+  unbind: function unbind() {
+    this.invalidatePending();
+    // Do not defer cleanup when unbinding
+    this.unbuild();
+    // destroy all keep-alive cached instances
+    if (this.cache) {
+      for (var key in this.cache) {
+        this.cache[key].$destroy();
+      }
+      this.cache = null;
+    }
+  }
+};
+
+/**
+ * Call activate hooks in order (asynchronous)
+ *
+ * @param {Array} hooks
+ * @param {Vue} vm
+ * @param {Function} cb
+ */
+
+function callActivateHooks(hooks, vm, cb) {
+  var total = hooks.length;
+  var called = 0;
+  hooks[0].call(vm, next);
+  function next() {
+    if (++called >= total) {
+      cb();
+    } else {
+      hooks[called].call(vm, next);
+    }
+  }
+}
+
+var propBindingModes = config._propBindingModes;
+var empty = {};
+
+// regexes
+var identRE$1 = /^[$_a-zA-Z]+[\w$]*$/;
+var settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/;
+
+/**
+ * Compile props on a root element and return
+ * a props link function.
+ *
+ * @param {Element|DocumentFragment} el
+ * @param {Array} propOptions
+ * @param {Vue} vm
+ * @return {Function} propsLinkFn
+ */
+
+function compileProps(el, propOptions, vm) {
+  var props = [];
+  var propsData = vm.$options.propsData;
+  var names = Object.keys(propOptions);
+  var i = names.length;
+  var options, name, attr, value, path, parsed, prop;
+  while (i--) {
+    name = names[i];
+    options = propOptions[name] || empty;
+
+    if ('development' !== 'production' && name === '$data') {
+      warn('Do not use $data as prop.', vm);
+      continue;
+    }
+
+    // props could contain dashes, which will be
+    // interpreted as minus calculations by the parser
+    // so we need to camelize the path here
+    path = camelize(name);
+    if (!identRE$1.test(path)) {
+      'development' !== 'production' && warn('Invalid prop key: "' + name + '". Prop keys ' + 'must be valid identifiers.', vm);
+      continue;
+    }
+
+    prop = {
+      name: name,
+      path: path,
+      options: options,
+      mode: propBindingModes.ONE_WAY,
+      raw: null
+    };
+
+    attr = hyphenate(name);
+    // first check dynamic version
+    if ((value = getBindAttr(el, attr)) === null) {
+      if ((value = getBindAttr(el, attr + '.sync')) !== null) {
+        prop.mode = propBindingModes.TWO_WAY;
+      } else if ((value = getBindAttr(el, attr + '.once')) !== null) {
+        prop.mode = propBindingModes.ONE_TIME;
+      }
+    }
+    if (value !== null) {
+      // has dynamic binding!
+      prop.raw = value;
+      parsed = parseDirective(value);
+      value = parsed.expression;
+      prop.filters = parsed.filters;
+      // check binding type
+      if (isLiteral(value) && !parsed.filters) {
+        // for expressions containing literal numbers and
+        // booleans, there's no need to setup a prop binding,
+        // so we can optimize them as a one-time set.
+        prop.optimizedLiteral = true;
+      } else {
+        prop.dynamic = true;
+        // check non-settable path for two-way bindings
+        if ('development' !== 'production' && prop.mode === propBindingModes.TWO_WAY && !settablePathRE.test(value)) {
+          prop.mode = propBindingModes.ONE_WAY;
+          warn('Cannot bind two-way prop with non-settable ' + 'parent path: ' + value, vm);
+        }
+      }
+      prop.parentPath = value;
+
+      // warn required two-way
+      if ('development' !== 'production' && options.twoWay && prop.mode !== propBindingModes.TWO_WAY) {
+        warn('Prop "' + name + '" expects a two-way binding type.', vm);
+      }
+    } else if ((value = getAttr(el, attr)) !== null) {
+      // has literal binding!
+      prop.raw = value;
+    } else if (propsData && (value = propsData[name] || propsData[path]) !== null) {
+      // has propsData
+      prop.raw = value;
+    } else if ('development' !== 'production') {
+      // check possible camelCase prop usage
+      var lowerCaseName = path.toLowerCase();
+      value = /[A-Z\-]/.test(name) && (el.getAttribute(lowerCaseName) || el.getAttribute(':' + lowerCaseName) || el.getAttribute('v-bind:' + lowerCaseName) || el.getAttribute(':' + lowerCaseName + '.once') || el.getAttribute('v-bind:' + lowerCaseName + '.once') || el.getAttribute(':' + lowerCaseName + '.sync') || el.getAttribute('v-bind:' + lowerCaseName + '.sync'));
+      if (value) {
+        warn('Possible usage error for prop `' + lowerCaseName + '` - ' + 'did you mean `' + attr + '`? HTML is case-insensitive, remember to use ' + 'kebab-case for props in templates.', vm);
+      } else if (options.required && (!propsData || !(name in propsData) && !(path in propsData))) {
+        // warn missing required
+        warn('Missing required prop: ' + name, vm);
+      }
+    }
+    // push prop
+    props.push(prop);
+  }
+  return makePropsLinkFn(props);
+}
+
+/**
+ * Build a function that applies props to a vm.
+ *
+ * @param {Array} props
+ * @return {Function} propsLinkFn
+ */
+
+function makePropsLinkFn(props) {
+  return function propsLinkFn(vm, scope) {
+    // store resolved props info
+    vm._props = {};
+    var inlineProps = vm.$options.propsData;
+    var i = props.length;
+    var prop, path, options, value, raw;
+    while (i--) {
+      prop = props[i];
+      raw = prop.raw;
+      path = prop.path;
+      options = prop.options;
+      vm._props[path] = prop;
+      if (inlineProps && hasOwn(inlineProps, path)) {
+        initProp(vm, prop, inlineProps[path]);
+      }if (raw === null) {
+        // initialize absent prop
+        initProp(vm, prop, undefined);
+      } else if (prop.dynamic) {
+        // dynamic prop
+        if (prop.mode === propBindingModes.ONE_TIME) {
+          // one time binding
+          value = (scope || vm._context || vm).$get(prop.parentPath);
+          initProp(vm, prop, value);
+        } else {
+          if (vm._context) {
+            // dynamic binding
+            vm._bindDir({
+              name: 'prop',
+              def: propDef,
+              prop: prop
+            }, null, null, scope); // el, host, scope
+          } else {
+              // root instance
+              initProp(vm, prop, vm.$get(prop.parentPath));
+            }
+        }
+      } else if (prop.optimizedLiteral) {
+        // optimized literal, cast it and just set once
+        var stripped = stripQuotes(raw);
+        value = stripped === raw ? toBoolean(toNumber(raw)) : stripped;
+        initProp(vm, prop, value);
+      } else {
+        // string literal, but we need to cater for
+        // Boolean props with no value, or with same
+        // literal value (e.g. disabled="disabled")
+        // see https://github.com/vuejs/vue-loader/issues/182
+        value = options.type === Boolean && (raw === '' || raw === hyphenate(prop.name)) ? true : raw;
+        initProp(vm, prop, value);
+      }
+    }
+  };
+}
+
+/**
+ * Process a prop with a rawValue, applying necessary coersions,
+ * default values & assertions and call the given callback with
+ * processed value.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @param {*} rawValue
+ * @param {Function} fn
+ */
+
+function processPropValue(vm, prop, rawValue, fn) {
+  var isSimple = prop.dynamic && isSimplePath(prop.parentPath);
+  var value = rawValue;
+  if (value === undefined) {
+    value = getPropDefaultValue(vm, prop);
+  }
+  value = coerceProp(prop, value, vm);
+  var coerced = value !== rawValue;
+  if (!assertProp(prop, value, vm)) {
+    value = undefined;
+  }
+  if (isSimple && !coerced) {
+    withoutConversion(function () {
+      fn(value);
+    });
+  } else {
+    fn(value);
+  }
+}
+
+/**
+ * Set a prop's initial value on a vm and its data object.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @param {*} value
+ */
+
+function initProp(vm, prop, value) {
+  processPropValue(vm, prop, value, function (value) {
+    defineReactive(vm, prop.path, value);
+  });
+}
+
+/**
+ * Update a prop's value on a vm.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @param {*} value
+ */
+
+function updateProp(vm, prop, value) {
+  processPropValue(vm, prop, value, function (value) {
+    vm[prop.path] = value;
+  });
+}
+
+/**
+ * Get the default value of a prop.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @return {*}
+ */
+
+function getPropDefaultValue(vm, prop) {
+  // no default, return undefined
+  var options = prop.options;
+  if (!hasOwn(options, 'default')) {
+    // absent boolean value defaults to false
+    return options.type === Boolean ? false : undefined;
+  }
+  var def = options['default'];
+  // warn against non-factory defaults for Object & Array
+  if (isObject(def)) {
+    'development' !== 'production' && warn('Invalid default value for prop "' + prop.name + '": ' + 'Props with type Object/Array must use a factory function ' + 'to return the default value.', vm);
+  }
+  // call factory function for non-Function types
+  return typeof def === 'function' && options.type !== Function ? def.call(vm) : def;
+}
+
+/**
+ * Assert whether a prop is valid.
+ *
+ * @param {Object} prop
+ * @param {*} value
+ * @param {Vue} vm
+ */
+
+function assertProp(prop, value, vm) {
+  if (!prop.options.required && ( // non-required
+  prop.raw === null || // abscent
+  value == null) // null or undefined
+  ) {
+      return true;
+    }
+  var options = prop.options;
+  var type = options.type;
+  var valid = !type;
+  var expectedTypes = [];
+  if (type) {
+    if (!isArray(type)) {
+      type = [type];
+    }
+    for (var i = 0; i < type.length && !valid; i++) {
+      var assertedType = assertType(value, type[i]);
+      expectedTypes.push(assertedType.expectedType);
+      valid = assertedType.valid;
+    }
+  }
+  if (!valid) {
+    if ('development' !== 'production') {
+      warn('Invalid prop: type check failed for prop "' + prop.name + '".' + ' Expected ' + expectedTypes.map(formatType).join(', ') + ', got ' + formatValue(value) + '.', vm);
+    }
+    return false;
+  }
+  var validator = options.validator;
+  if (validator) {
+    if (!validator(value)) {
+      'development' !== 'production' && warn('Invalid prop: custom validator check failed for prop "' + prop.name + '".', vm);
+      return false;
+    }
+  }
+  return true;
+}
+
+/**
+ * Force parsing value with coerce option.
+ *
+ * @param {*} value
+ * @param {Object} options
+ * @return {*}
+ */
+
+function coerceProp(prop, value, vm) {
+  var coerce = prop.options.coerce;
+  if (!coerce) {
+    return value;
+  }
+  if (typeof coerce === 'function') {
+    return coerce(value);
+  } else {
+    'development' !== 'production' && warn('Invalid coerce for prop "' + prop.name + '": expected function, got ' + typeof coerce + '.', vm);
+    return value;
+  }
+}
+
+/**
+ * Assert the type of a value
+ *
+ * @param {*} value
+ * @param {Function} type
+ * @return {Object}
+ */
+
+function assertType(value, type) {
+  var valid;
+  var expectedType;
+  if (type === String) {
+    expectedType = 'string';
+    valid = typeof value === expectedType;
+  } else if (type === Number) {
+    expectedType = 'number';
+    valid = typeof value === expectedType;
+  } else if (type === Boolean) {
+    expectedType = 'boolean';
+    valid = typeof value === expectedType;
+  } else if (type === Function) {
+    expectedType = 'function';
+    valid = typeof value === expectedType;
+  } else if (type === Object) {
+    expectedType = 'object';
+    valid = isPlainObject(value);
+  } else if (type === Array) {
+    expectedType = 'array';
+    valid = isArray(value);
+  } else {
+    valid = value instanceof type;
+  }
+  return {
+    valid: valid,
+    expectedType: expectedType
+  };
+}
+
+/**
+ * Format type for output
+ *
+ * @param {String} type
+ * @return {String}
+ */
+
+function formatType(type) {
+  return type ? type.charAt(0).toUpperCase() + type.slice(1) : 'custom type';
+}
+
+/**
+ * Format value
+ *
+ * @param {*} value
+ * @return {String}
+ */
+
+function formatValue(val) {
+  return Object.prototype.toString.call(val).slice(8, -1);
+}
+
+var bindingModes = config._propBindingModes;
+
+var propDef = {
+
+  bind: function bind() {
+    var child = this.vm;
+    var parent = child._context;
+    // passed in from compiler directly
+    var prop = this.descriptor.prop;
+    var childKey = prop.path;
+    var parentKey = prop.parentPath;
+    var twoWay = prop.mode === bindingModes.TWO_WAY;
+
+    var parentWatcher = this.parentWatcher = new Watcher(parent, parentKey, function (val) {
+      updateProp(child, prop, val);
+    }, {
+      twoWay: twoWay,
+      filters: prop.filters,
+      // important: props need to be observed on the
+      // v-for scope if present
+      scope: this._scope
+    });
+
+    // set the child initial value.
+    initProp(child, prop, parentWatcher.value);
+
+    // setup two-way binding
+    if (twoWay) {
+      // important: defer the child watcher creation until
+      // the created hook (after data observation)
+      var self = this;
+      child.$once('pre-hook:created', function () {
+        self.childWatcher = new Watcher(child, childKey, function (val) {
+          parentWatcher.set(val);
+        }, {
+          // ensure sync upward before parent sync down.
+          // this is necessary in cases e.g. the child
+          // mutates a prop array, then replaces it. (#1683)
+          sync: true
+        });
+      });
+    }
+  },
+
+  unbind: function unbind() {
+    this.parentWatcher.teardown();
+    if (this.childWatcher) {
+      this.childWatcher.teardown();
+    }
+  }
+};
+
+var queue$1 = [];
+var queued = false;
+
+/**
+ * Push a job into the queue.
+ *
+ * @param {Function} job
+ */
+
+function pushJob(job) {
+  queue$1.push(job);
+  if (!queued) {
+    queued = true;
+    nextTick(flush);
+  }
+}
+
+/**
+ * Flush the queue, and do one forced reflow before
+ * triggering transitions.
+ */
+
+function flush() {
+  // Force layout
+  var f = document.documentElement.offsetHeight;
+  for (var i = 0; i < queue$1.length; i++) {
+    queue$1[i]();
+  }
+  queue$1 = [];
+  queued = false;
+  // dummy return, so js linters don't complain about
+  // unused variable f
+  return f;
+}
+
+var TYPE_TRANSITION = 'transition';
+var TYPE_ANIMATION = 'animation';
+var transDurationProp = transitionProp + 'Duration';
+var animDurationProp = animationProp + 'Duration';
+
+/**
+ * If a just-entered element is applied the
+ * leave class while its enter transition hasn't started yet,
+ * and the transitioned property has the same value for both
+ * enter/leave, then the leave transition will be skipped and
+ * the transitionend event never fires. This function ensures
+ * its callback to be called after a transition has started
+ * by waiting for double raf.
+ *
+ * It falls back to setTimeout on devices that support CSS
+ * transitions but not raf (e.g. Android 4.2 browser) - since
+ * these environments are usually slow, we are giving it a
+ * relatively large timeout.
+ */
+
+var raf = inBrowser && window.requestAnimationFrame;
+var waitForTransitionStart = raf
+/* istanbul ignore next */
+? function (fn) {
+  raf(function () {
+    raf(fn);
+  });
+} : function (fn) {
+  setTimeout(fn, 50);
+};
+
+/**
+ * A Transition object that encapsulates the state and logic
+ * of the transition.
+ *
+ * @param {Element} el
+ * @param {String} id
+ * @param {Object} hooks
+ * @param {Vue} vm
+ */
+function Transition(el, id, hooks, vm) {
+  this.id = id;
+  this.el = el;
+  this.enterClass = hooks && hooks.enterClass || id + '-enter';
+  this.leaveClass = hooks && hooks.leaveClass || id + '-leave';
+  this.hooks = hooks;
+  this.vm = vm;
+  // async state
+  this.pendingCssEvent = this.pendingCssCb = this.cancel = this.pendingJsCb = this.op = this.cb = null;
+  this.justEntered = false;
+  this.entered = this.left = false;
+  this.typeCache = {};
+  // check css transition type
+  this.type = hooks && hooks.type;
+  /* istanbul ignore if */
+  if ('development' !== 'production') {
+    if (this.type && this.type !== TYPE_TRANSITION && this.type !== TYPE_ANIMATION) {
+      warn('invalid CSS transition type for transition="' + this.id + '": ' + this.type, vm);
+    }
+  }
+  // bind
+  var self = this;['enterNextTick', 'enterDone', 'leaveNextTick', 'leaveDone'].forEach(function (m) {
+    self[m] = bind(self[m], self);
+  });
+}
+
+var p$1 = Transition.prototype;
+
+/**
+ * Start an entering transition.
+ *
+ * 1. enter transition triggered
+ * 2. call beforeEnter hook
+ * 3. add enter class
+ * 4. insert/show element
+ * 5. call enter hook (with possible explicit js callback)
+ * 6. reflow
+ * 7. based on transition type:
+ *    - transition:
+ *        remove class now, wait for transitionend,
+ *        then done if there's no explicit js callback.
+ *    - animation:
+ *        wait for animationend, remove class,
+ *        then done if there's no explicit js callback.
+ *    - no css transition:
+ *        done now if there's no explicit js callback.
+ * 8. wait for either done or js callback, then call
+ *    afterEnter hook.
+ *
+ * @param {Function} op - insert/show the element
+ * @param {Function} [cb]
+ */
+
+p$1.enter = function (op, cb) {
+  this.cancelPending();
+  this.callHook('beforeEnter');
+  this.cb = cb;
+  addClass(this.el, this.enterClass);
+  op();
+  this.entered = false;
+  this.callHookWithCb('enter');
+  if (this.entered) {
+    return; // user called done synchronously.
+  }
+  this.cancel = this.hooks && this.hooks.enterCancelled;
+  pushJob(this.enterNextTick);
+};
+
+/**
+ * The "nextTick" phase of an entering transition, which is
+ * to be pushed into a queue and executed after a reflow so
+ * that removing the class can trigger a CSS transition.
+ */
+
+p$1.enterNextTick = function () {
+  var _this = this;
+
+  // prevent transition skipping
+  this.justEntered = true;
+  waitForTransitionStart(function () {
+    _this.justEntered = false;
+  });
+  var enterDone = this.enterDone;
+  var type = this.getCssTransitionType(this.enterClass);
+  if (!this.pendingJsCb) {
+    if (type === TYPE_TRANSITION) {
+      // trigger transition by removing enter class now
+      removeClass(this.el, this.enterClass);
+      this.setupCssCb(transitionEndEvent, enterDone);
+    } else if (type === TYPE_ANIMATION) {
+      this.setupCssCb(animationEndEvent, enterDone);
+    } else {
+      enterDone();
+    }
+  } else if (type === TYPE_TRANSITION) {
+    removeClass(this.el, this.enterClass);
+  }
+};
+
+/**
+ * The "cleanup" phase of an entering transition.
+ */
+
+p$1.enterDone = function () {
+  this.entered = true;
+  this.cancel = this.pendingJsCb = null;
+  removeClass(this.el, this.enterClass);
+  this.callHook('afterEnter');
+  if (this.cb) this.cb();
+};
+
+/**
+ * Start a leaving transition.
+ *
+ * 1. leave transition triggered.
+ * 2. call beforeLeave hook
+ * 3. add leave class (trigger css transition)
+ * 4. call leave hook (with possible explicit js callback)
+ * 5. reflow if no explicit js callback is provided
+ * 6. based on transition type:
+ *    - transition or animation:
+ *        wait for end event, remove class, then done if
+ *        there's no explicit js callback.
+ *    - no css transition:
+ *        done if there's no explicit js callback.
+ * 7. wait for either done or js callback, then call
+ *    afterLeave hook.
+ *
+ * @param {Function} op - remove/hide the element
+ * @param {Function} [cb]
+ */
+
+p$1.leave = function (op, cb) {
+  this.cancelPending();
+  this.callHook('beforeLeave');
+  this.op = op;
+  this.cb = cb;
+  addClass(this.el, this.leaveClass);
+  this.left = false;
+  this.callHookWithCb('leave');
+  if (this.left) {
+    return; // user called done synchronously.
+  }
+  this.cancel = this.hooks && this.hooks.leaveCancelled;
+  // only need to handle leaveDone if
+  // 1. the transition is already done (synchronously called
+  //    by the user, which causes this.op set to null)
+  // 2. there's no explicit js callback
+  if (this.op && !this.pendingJsCb) {
+    // if a CSS transition leaves immediately after enter,
+    // the transitionend event never fires. therefore we
+    // detect such cases and end the leave immediately.
+    if (this.justEntered) {
+      this.leaveDone();
+    } else {
+      pushJob(this.leaveNextTick);
+    }
+  }
+};
+
+/**
+ * The "nextTick" phase of a leaving transition.
+ */
+
+p$1.leaveNextTick = function () {
+  var type = this.getCssTransitionType(this.leaveClass);
+  if (type) {
+    var event = type === TYPE_TRANSITION ? transitionEndEvent : animationEndEvent;
+    this.setupCssCb(event, this.leaveDone);
+  } else {
+    this.leaveDone();
+  }
+};
+
+/**
+ * The "cleanup" phase of a leaving transition.
+ */
+
+p$1.leaveDone = function () {
+  this.left = true;
+  this.cancel = this.pendingJsCb = null;
+  this.op();
+  removeClass(this.el, this.leaveClass);
+  this.callHook('afterLeave');
+  if (this.cb) this.cb();
+  this.op = null;
+};
+
+/**
+ * Cancel any pending callbacks from a previously running
+ * but not finished transition.
+ */
+
+p$1.cancelPending = function () {
+  this.op = this.cb = null;
+  var hasPending = false;
+  if (this.pendingCssCb) {
+    hasPending = true;
+    off(this.el, this.pendingCssEvent, this.pendingCssCb);
+    this.pendingCssEvent = this.pendingCssCb = null;
+  }
+  if (this.pendingJsCb) {
+    hasPending = true;
+    this.pendingJsCb.cancel();
+    this.pendingJsCb = null;
+  }
+  if (hasPending) {
+    removeClass(this.el, this.enterClass);
+    removeClass(this.el, this.leaveClass);
+  }
+  if (this.cancel) {
+    this.cancel.call(this.vm, this.el);
+    this.cancel = null;
+  }
+};
+
+/**
+ * Call a user-provided synchronous hook function.
+ *
+ * @param {String} type
+ */
+
+p$1.callHook = function (type) {
+  if (this.hooks && this.hooks[type]) {
+    this.hooks[type].call(this.vm, this.el);
+  }
+};
+
+/**
+ * Call a user-provided, potentially-async hook function.
+ * We check for the length of arguments to see if the hook
+ * expects a `done` callback. If true, the transition's end
+ * will be determined by when the user calls that callback;
+ * otherwise, the end is determined by the CSS transition or
+ * animation.
+ *
+ * @param {String} type
+ */
+
+p$1.callHookWithCb = function (type) {
+  var hook = this.hooks && this.hooks[type];
+  if (hook) {
+    if (hook.length > 1) {
+      this.pendingJsCb = cancellable(this[type + 'Done']);
+    }
+    hook.call(this.vm, this.el, this.pendingJsCb);
+  }
+};
+
+/**
+ * Get an element's transition type based on the
+ * calculated styles.
+ *
+ * @param {String} className
+ * @return {Number}
+ */
+
+p$1.getCssTransitionType = function (className) {
+  /* istanbul ignore if */
+  if (!transitionEndEvent ||
+  // skip CSS transitions if page is not visible -
+  // this solves the issue of transitionend events not
+  // firing until the page is visible again.
+  // pageVisibility API is supported in IE10+, same as
+  // CSS transitions.
+  document.hidden ||
+  // explicit js-only transition
+  this.hooks && this.hooks.css === false ||
+  // element is hidden
+  isHidden(this.el)) {
+    return;
+  }
+  var type = this.type || this.typeCache[className];
+  if (type) return type;
+  var inlineStyles = this.el.style;
+  var computedStyles = window.getComputedStyle(this.el);
+  var transDuration = inlineStyles[transDurationProp] || computedStyles[transDurationProp];
+  if (transDuration && transDuration !== '0s') {
+    type = TYPE_TRANSITION;
+  } else {
+    var animDuration = inlineStyles[animDurationProp] || computedStyles[animDurationProp];
+    if (animDuration && animDuration !== '0s') {
+      type = TYPE_ANIMATION;
+    }
+  }
+  if (type) {
+    this.typeCache[className] = type;
+  }
+  return type;
+};
+
+/**
+ * Setup a CSS transitionend/animationend callback.
+ *
+ * @param {String} event
+ * @param {Function} cb
+ */
+
+p$1.setupCssCb = function (event, cb) {
+  this.pendingCssEvent = event;
+  var self = this;
+  var el = this.el;
+  var onEnd = this.pendingCssCb = function (e) {
+    if (e.target === el) {
+      off(el, event, onEnd);
+      self.pendingCssEvent = self.pendingCssCb = null;
+      if (!self.pendingJsCb && cb) {
+        cb();
+      }
+    }
+  };
+  on(el, event, onEnd);
+};
+
+/**
+ * Check if an element is hidden - in that case we can just
+ * skip the transition alltogether.
+ *
+ * @param {Element} el
+ * @return {Boolean}
+ */
+
+function isHidden(el) {
+  if (/svg$/.test(el.namespaceURI)) {
+    // SVG elements do not have offset(Width|Height)
+    // so we need to check the client rect
+    var rect = el.getBoundingClientRect();
+    return !(rect.width || rect.height);
+  } else {
+    return !(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
+  }
+}
+
+var transition$1 = {
+
+  priority: TRANSITION,
+
+  update: function update(id, oldId) {
+    var el = this.el;
+    // resolve on owner vm
+    var hooks = resolveAsset(this.vm.$options, 'transitions', id);
+    id = id || 'v';
+    oldId = oldId || 'v';
+    el.__v_trans = new Transition(el, id, hooks, this.vm);
+    removeClass(el, oldId + '-transition');
+    addClass(el, id + '-transition');
+  }
+};
+
+var internalDirectives = {
+  style: style,
+  'class': vClass,
+  component: component,
+  prop: propDef,
+  transition: transition$1
+};
+
+// special binding prefixes
+var bindRE = /^v-bind:|^:/;
+var onRE = /^v-on:|^@/;
+var dirAttrRE = /^v-([^:]+)(?:$|:(.*)$)/;
+var modifierRE = /\.[^\.]+/g;
+var transitionRE = /^(v-bind:|:)?transition$/;
+
+// default directive priority
+var DEFAULT_PRIORITY = 1000;
+var DEFAULT_TERMINAL_PRIORITY = 2000;
+
+/**
+ * Compile a template and return a reusable composite link
+ * function, which recursively contains more link functions
+ * inside. This top level compile function would normally
+ * be called on instance root nodes, but can also be used
+ * for partial compilation if the partial argument is true.
+ *
+ * The returned composite link function, when called, will
+ * return an unlink function that tearsdown all directives
+ * created during the linking phase.
+ *
+ * @param {Element|DocumentFragment} el
+ * @param {Object} options
+ * @param {Boolean} partial
+ * @return {Function}
+ */
+
+function compile(el, options, partial) {
+  // link function for the node itself.
+  var nodeLinkFn = partial || !options._asComponent ? compileNode(el, options) : null;
+  // link function for the childNodes
+  var childLinkFn = !(nodeLinkFn && nodeLinkFn.terminal) && !isScript(el) && el.hasChildNodes() ? compileNodeList(el.childNodes, options) : null;
+
+  /**
+   * A composite linker function to be called on a already
+   * compiled piece of DOM, which instantiates all directive
+   * instances.
+   *
+   * @param {Vue} vm
+   * @param {Element|DocumentFragment} el
+   * @param {Vue} [host] - host vm of transcluded content
+   * @param {Object} [scope] - v-for scope
+   * @param {Fragment} [frag] - link context fragment
+   * @return {Function|undefined}
+   */
+
+  return function compositeLinkFn(vm, el, host, scope, frag) {
+    // cache childNodes before linking parent, fix #657
+    var childNodes = toArray(el.childNodes);
+    // link
+    var dirs = linkAndCapture(function compositeLinkCapturer() {
+      if (nodeLinkFn) nodeLinkFn(vm, el, host, scope, frag);
+      if (childLinkFn) childLinkFn(vm, childNodes, host, scope, frag);
+    }, vm);
+    return makeUnlinkFn(vm, dirs);
+  };
+}
+
+/**
+ * Apply a linker to a vm/element pair and capture the
+ * directives created during the process.
+ *
+ * @param {Function} linker
+ * @param {Vue} vm
+ */
+
+function linkAndCapture(linker, vm) {
+  /* istanbul ignore if */
+  if ('development' === 'production') {}
+  var originalDirCount = vm._directives.length;
+  linker();
+  var dirs = vm._directives.slice(originalDirCount);
+  sortDirectives(dirs);
+  for (var i = 0, l = dirs.length; i < l; i++) {
+    dirs[i]._bind();
+  }
+  return dirs;
+}
+
+/**
+ * sort directives by priority (stable sort)
+ *
+ * @param {Array} dirs
+ */
+function sortDirectives(dirs) {
+  if (dirs.length === 0) return;
+
+  var groupedMap = {};
+  var i, j, k, l;
+  var index = 0;
+  var priorities = [];
+  for (i = 0, j = dirs.length; i < j; i++) {
+    var dir = dirs[i];
+    var priority = dir.descriptor.def.priority || DEFAULT_PRIORITY;
+    var array = groupedMap[priority];
+    if (!array) {
+      array = groupedMap[priority] = [];
+      priorities.push(priority);
+    }
+    array.push(dir);
+  }
+
+  priorities.sort(function (a, b) {
+    return a > b ? -1 : a === b ? 0 : 1;
+  });
+  for (i = 0, j = priorities.length; i < j; i++) {
+    var group = groupedMap[priorities[i]];
+    for (k = 0, l = group.length; k < l; k++) {
+      dirs[index++] = group[k];
+    }
+  }
+}
+
+/**
+ * Linker functions return an unlink function that
+ * tearsdown all directives instances generated during
+ * the process.
+ *
+ * We create unlink functions with only the necessary
+ * information to avoid retaining additional closures.
+ *
+ * @param {Vue} vm
+ * @param {Array} dirs
+ * @param {Vue} [context]
+ * @param {Array} [contextDirs]
+ * @return {Function}
+ */
+
+function makeUnlinkFn(vm, dirs, context, contextDirs) {
+  function unlink(destroying) {
+    teardownDirs(vm, dirs, destroying);
+    if (context && contextDirs) {
+      teardownDirs(context, contextDirs);
+    }
+  }
+  // expose linked directives
+  unlink.dirs = dirs;
+  return unlink;
+}
+
+/**
+ * Teardown partial linked directives.
+ *
+ * @param {Vue} vm
+ * @param {Array} dirs
+ * @param {Boolean} destroying
+ */
+
+function teardownDirs(vm, dirs, destroying) {
+  var i = dirs.length;
+  while (i--) {
+    dirs[i]._teardown();
+    if ('development' !== 'production' && !destroying) {
+      vm._directives.$remove(dirs[i]);
+    }
+  }
+}
+
+/**
+ * Compile link props on an instance.
+ *
+ * @param {Vue} vm
+ * @param {Element} el
+ * @param {Object} props
+ * @param {Object} [scope]
+ * @return {Function}
+ */
+
+function compileAndLinkProps(vm, el, props, scope) {
+  var propsLinkFn = compileProps(el, props, vm);
+  var propDirs = linkAndCapture(function () {
+    propsLinkFn(vm, scope);
+  }, vm);
+  return makeUnlinkFn(vm, propDirs);
+}
+
+/**
+ * Compile the root element of an instance.
+ *
+ * 1. attrs on context container (context scope)
+ * 2. attrs on the component template root node, if
+ *    replace:true (child scope)
+ *
+ * If this is a fragment instance, we only need to compile 1.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @param {Object} contextOptions
+ * @return {Function}
+ */
+
+function compileRoot(el, options, contextOptions) {
+  var containerAttrs = options._containerAttrs;
+  var replacerAttrs = options._replacerAttrs;
+  var contextLinkFn, replacerLinkFn;
+
+  // only need to compile other attributes for
+  // non-fragment instances
+  if (el.nodeType !== 11) {
+    // for components, container and replacer need to be
+    // compiled separately and linked in different scopes.
+    if (options._asComponent) {
+      // 2. container attributes
+      if (containerAttrs && contextOptions) {
+        contextLinkFn = compileDirectives(containerAttrs, contextOptions);
+      }
+      if (replacerAttrs) {
+        // 3. replacer attributes
+        replacerLinkFn = compileDirectives(replacerAttrs, options);
+      }
+    } else {
+      // non-component, just compile as a normal element.
+      replacerLinkFn = compileDirectives(el.attributes, options);
+    }
+  } else if ('development' !== 'production' && containerAttrs) {
+    // warn container directives for fragment instances
+    var names = containerAttrs.filter(function (attr) {
+      // allow vue-loader/vueify scoped css attributes
+      return attr.name.indexOf('_v-') < 0 &&
+      // allow event listeners
+      !onRE.test(attr.name) &&
+      // allow slots
+      attr.name !== 'slot';
+    }).map(function (attr) {
+      return '"' + attr.name + '"';
+    });
+    if (names.length) {
+      var plural = names.length > 1;
+
+      var componentName = options.el.tagName.toLowerCase();
+      if (componentName === 'component' && options.name) {
+        componentName += ':' + options.name;
+      }
+
+      warn('Attribute' + (plural ? 's ' : ' ') + names.join(', ') + (plural ? ' are' : ' is') + ' ignored on component ' + '<' + componentName + '> because ' + 'the component is a fragment instance: ' + 'http://vuejs.org/guide/components.html#Fragment-Instance');
+    }
+  }
+
+  options._containerAttrs = options._replacerAttrs = null;
+  return function rootLinkFn(vm, el, scope) {
+    // link context scope dirs
+    var context = vm._context;
+    var contextDirs;
+    if (context && contextLinkFn) {
+      contextDirs = linkAndCapture(function () {
+        contextLinkFn(context, el, null, scope);
+      }, context);
+    }
+
+    // link self
+    var selfDirs = linkAndCapture(function () {
+      if (replacerLinkFn) replacerLinkFn(vm, el);
+    }, vm);
+
+    // return the unlink function that tearsdown context
+    // container directives.
+    return makeUnlinkFn(vm, selfDirs, context, contextDirs);
+  };
+}
+
+/**
+ * Compile a node and return a nodeLinkFn based on the
+ * node type.
+ *
+ * @param {Node} node
+ * @param {Object} options
+ * @return {Function|null}
+ */
+
+function compileNode(node, options) {
+  var type = node.nodeType;
+  if (type === 1 && !isScript(node)) {
+    return compileElement(node, options);
+  } else if (type === 3 && node.data.trim()) {
+    return compileTextNode(node, options);
+  } else {
+    return null;
+  }
+}
+
+/**
+ * Compile an element and return a nodeLinkFn.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Function|null}
+ */
+
+function compileElement(el, options) {
+  // preprocess textareas.
+  // textarea treats its text content as the initial value.
+  // just bind it as an attr directive for value.
+  if (el.tagName === 'TEXTAREA') {
+    // a textarea which has v-pre attr should skip complie.
+    if (getAttr(el, 'v-pre') !== null) {
+      return skip;
+    }
+    var tokens = parseText(el.value);
+    if (tokens) {
+      el.setAttribute(':value', tokensToExp(tokens));
+      el.value = '';
+    }
+  }
+  var linkFn;
+  var hasAttrs = el.hasAttributes();
+  var attrs = hasAttrs && toArray(el.attributes);
+  // check terminal directives (for & if)
+  if (hasAttrs) {
+    linkFn = checkTerminalDirectives(el, attrs, options);
+  }
+  // check element directives
+  if (!linkFn) {
+    linkFn = checkElementDirectives(el, options);
+  }
+  // check component
+  if (!linkFn) {
+    linkFn = checkComponent(el, options);
+  }
+  // normal directives
+  if (!linkFn && hasAttrs) {
+    linkFn = compileDirectives(attrs, options);
+  }
+  return linkFn;
+}
+
+/**
+ * Compile a textNode and return a nodeLinkFn.
+ *
+ * @param {TextNode} node
+ * @param {Object} options
+ * @return {Function|null} textNodeLinkFn
+ */
+
+function compileTextNode(node, options) {
+  // skip marked text nodes
+  if (node._skip) {
+    return removeText;
+  }
+
+  var tokens = parseText(node.wholeText);
+  if (!tokens) {
+    return null;
+  }
+
+  // mark adjacent text nodes as skipped,
+  // because we are using node.wholeText to compile
+  // all adjacent text nodes together. This fixes
+  // issues in IE where sometimes it splits up a single
+  // text node into multiple ones.
+  var next = node.nextSibling;
+  while (next && next.nodeType === 3) {
+    next._skip = true;
+    next = next.nextSibling;
+  }
+
+  var frag = document.createDocumentFragment();
+  var el, token;
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    token = tokens[i];
+    el = token.tag ? processTextToken(token, options) : document.createTextNode(token.value);
+    frag.appendChild(el);
+  }
+  return makeTextNodeLinkFn(tokens, frag, options);
+}
+
+/**
+ * Linker for an skipped text node.
+ *
+ * @param {Vue} vm
+ * @param {Text} node
+ */
+
+function removeText(vm, node) {
+  remove(node);
+}
+
+/**
+ * Process a single text token.
+ *
+ * @param {Object} token
+ * @param {Object} options
+ * @return {Node}
+ */
+
+function processTextToken(token, options) {
+  var el;
+  if (token.oneTime) {
+    el = document.createTextNode(token.value);
+  } else {
+    if (token.html) {
+      el = document.createComment('v-html');
+      setTokenType('html');
+    } else {
+      // IE will clean up empty textNodes during
+      // frag.cloneNode(true), so we have to give it
+      // something here...
+      el = document.createTextNode(' ');
+      setTokenType('text');
+    }
+  }
+  function setTokenType(type) {
+    if (token.descriptor) return;
+    var parsed = parseDirective(token.value);
+    token.descriptor = {
+      name: type,
+      def: directives[type],
+      expression: parsed.expression,
+      filters: parsed.filters
+    };
+  }
+  return el;
+}
+
+/**
+ * Build a function that processes a textNode.
+ *
+ * @param {Array<Object>} tokens
+ * @param {DocumentFragment} frag
+ */
+
+function makeTextNodeLinkFn(tokens, frag) {
+  return function textNodeLinkFn(vm, el, host, scope) {
+    var fragClone = frag.cloneNode(true);
+    var childNodes = toArray(fragClone.childNodes);
+    var token, value, node;
+    for (var i = 0, l = tokens.length; i < l; i++) {
+      token = tokens[i];
+      value = token.value;
+      if (token.tag) {
+        node = childNodes[i];
+        if (token.oneTime) {
+          value = (scope || vm).$eval(value);
+          if (token.html) {
+            replace(node, parseTemplate(value, true));
+          } else {
+            node.data = _toString(value);
+          }
+        } else {
+          vm._bindDir(token.descriptor, node, host, scope);
+        }
+      }
+    }
+    replace(el, fragClone);
+  };
+}
+
+/**
+ * Compile a node list and return a childLinkFn.
+ *
+ * @param {NodeList} nodeList
+ * @param {Object} options
+ * @return {Function|undefined}
+ */
+
+function compileNodeList(nodeList, options) {
+  var linkFns = [];
+  var nodeLinkFn, childLinkFn, node;
+  for (var i = 0, l = nodeList.length; i < l; i++) {
+    node = nodeList[i];
+    nodeLinkFn = compileNode(node, options);
+    childLinkFn = !(nodeLinkFn && nodeLinkFn.terminal) && node.tagName !== 'SCRIPT' && node.hasChildNodes() ? compileNodeList(node.childNodes, options) : null;
+    linkFns.push(nodeLinkFn, childLinkFn);
+  }
+  return linkFns.length ? makeChildLinkFn(linkFns) : null;
+}
+
+/**
+ * Make a child link function for a node's childNodes.
+ *
+ * @param {Array<Function>} linkFns
+ * @return {Function} childLinkFn
+ */
+
+function makeChildLinkFn(linkFns) {
+  return function childLinkFn(vm, nodes, host, scope, frag) {
+    var node, nodeLinkFn, childrenLinkFn;
+    for (var i = 0, n = 0, l = linkFns.length; i < l; n++) {
+      node = nodes[n];
+      nodeLinkFn = linkFns[i++];
+      childrenLinkFn = linkFns[i++];
+      // cache childNodes before linking parent, fix #657
+      var childNodes = toArray(node.childNodes);
+      if (nodeLinkFn) {
+        nodeLinkFn(vm, node, host, scope, frag);
+      }
+      if (childrenLinkFn) {
+        childrenLinkFn(vm, childNodes, host, scope, frag);
+      }
+    }
+  };
+}
+
+/**
+ * Check for element directives (custom elements that should
+ * be resovled as terminal directives).
+ *
+ * @param {Element} el
+ * @param {Object} options
+ */
+
+function checkElementDirectives(el, options) {
+  var tag = el.tagName.toLowerCase();
+  if (commonTagRE.test(tag)) {
+    return;
+  }
+  var def = resolveAsset(options, 'elementDirectives', tag);
+  if (def) {
+    return makeTerminalNodeLinkFn(el, tag, '', options, def);
+  }
+}
+
+/**
+ * Check if an element is a component. If yes, return
+ * a component link function.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Function|undefined}
+ */
+
+function checkComponent(el, options) {
+  var component = checkComponentAttr(el, options);
+  if (component) {
+    var ref = findRef(el);
+    var descriptor = {
+      name: 'component',
+      ref: ref,
+      expression: component.id,
+      def: internalDirectives.component,
+      modifiers: {
+        literal: !component.dynamic
+      }
+    };
+    var componentLinkFn = function componentLinkFn(vm, el, host, scope, frag) {
+      if (ref) {
+        defineReactive((scope || vm).$refs, ref, null);
+      }
+      vm._bindDir(descriptor, el, host, scope, frag);
+    };
+    componentLinkFn.terminal = true;
+    return componentLinkFn;
+  }
+}
+
+/**
+ * Check an element for terminal directives in fixed order.
+ * If it finds one, return a terminal link function.
+ *
+ * @param {Element} el
+ * @param {Array} attrs
+ * @param {Object} options
+ * @return {Function} terminalLinkFn
+ */
+
+function checkTerminalDirectives(el, attrs, options) {
+  // skip v-pre
+  if (getAttr(el, 'v-pre') !== null) {
+    return skip;
+  }
+  // skip v-else block, but only if following v-if
+  if (el.hasAttribute('v-else')) {
+    var prev = el.previousElementSibling;
+    if (prev && prev.hasAttribute('v-if')) {
+      return skip;
+    }
+  }
+
+  var attr, name, value, modifiers, matched, dirName, rawName, arg, def, termDef;
+  for (var i = 0, j = attrs.length; i < j; i++) {
+    attr = attrs[i];
+    name = attr.name.replace(modifierRE, '');
+    if (matched = name.match(dirAttrRE)) {
+      def = resolveAsset(options, 'directives', matched[1]);
+      if (def && def.terminal) {
+        if (!termDef || (def.priority || DEFAULT_TERMINAL_PRIORITY) > termDef.priority) {
+          termDef = def;
+          rawName = attr.name;
+          modifiers = parseModifiers(attr.name);
+          value = attr.value;
+          dirName = matched[1];
+          arg = matched[2];
+        }
+      }
+    }
+  }
+
+  if (termDef) {
+    return makeTerminalNodeLinkFn(el, dirName, value, options, termDef, rawName, arg, modifiers);
+  }
+}
+
+function skip() {}
+skip.terminal = true;
+
+/**
+ * Build a node link function for a terminal directive.
+ * A terminal link function terminates the current
+ * compilation recursion and handles compilation of the
+ * subtree in the directive.
+ *
+ * @param {Element} el
+ * @param {String} dirName
+ * @param {String} value
+ * @param {Object} options
+ * @param {Object} def
+ * @param {String} [rawName]
+ * @param {String} [arg]
+ * @param {Object} [modifiers]
+ * @return {Function} terminalLinkFn
+ */
+
+function makeTerminalNodeLinkFn(el, dirName, value, options, def, rawName, arg, modifiers) {
+  var parsed = parseDirective(value);
+  var descriptor = {
+    name: dirName,
+    arg: arg,
+    expression: parsed.expression,
+    filters: parsed.filters,
+    raw: value,
+    attr: rawName,
+    modifiers: modifiers,
+    def: def
+  };
+  // check ref for v-for, v-if and router-view
+  if (dirName === 'for' || dirName === 'router-view') {
+    descriptor.ref = findRef(el);
+  }
+  var fn = function terminalNodeLinkFn(vm, el, host, scope, frag) {
+    if (descriptor.ref) {
+      defineReactive((scope || vm).$refs, descriptor.ref, null);
+    }
+    vm._bindDir(descriptor, el, host, scope, frag);
+  };
+  fn.terminal = true;
+  return fn;
+}
+
+/**
+ * Compile the directives on an element and return a linker.
+ *
+ * @param {Array|NamedNodeMap} attrs
+ * @param {Object} options
+ * @return {Function}
+ */
+
+function compileDirectives(attrs, options) {
+  var i = attrs.length;
+  var dirs = [];
+  var attr, name, value, rawName, rawValue, dirName, arg, modifiers, dirDef, tokens, matched;
+  while (i--) {
+    attr = attrs[i];
+    name = rawName = attr.name;
+    value = rawValue = attr.value;
+    tokens = parseText(value);
+    // reset arg
+    arg = null;
+    // check modifiers
+    modifiers = parseModifiers(name);
+    name = name.replace(modifierRE, '');
+
+    // attribute interpolations
+    if (tokens) {
+      value = tokensToExp(tokens);
+      arg = name;
+      pushDir('bind', directives.bind, tokens);
+      // warn against mixing mustaches with v-bind
+      if ('development' !== 'production') {
+        if (name === 'class' && Array.prototype.some.call(attrs, function (attr) {
+          return attr.name === ':class' || attr.name === 'v-bind:class';
+        })) {
+          warn('class="' + rawValue + '": Do not mix mustache interpolation ' + 'and v-bind for "class" on the same element. Use one or the other.', options);
+        }
+      }
+    } else
+
+      // special attribute: transition
+      if (transitionRE.test(name)) {
+        modifiers.literal = !bindRE.test(name);
+        pushDir('transition', internalDirectives.transition);
+      } else
+
+        // event handlers
+        if (onRE.test(name)) {
+          arg = name.replace(onRE, '');
+          pushDir('on', directives.on);
+        } else
+
+          // attribute bindings
+          if (bindRE.test(name)) {
+            dirName = name.replace(bindRE, '');
+            if (dirName === 'style' || dirName === 'class') {
+              pushDir(dirName, internalDirectives[dirName]);
+            } else {
+              arg = dirName;
+              pushDir('bind', directives.bind);
+            }
+          } else
+
+            // normal directives
+            if (matched = name.match(dirAttrRE)) {
+              dirName = matched[1];
+              arg = matched[2];
+
+              // skip v-else (when used with v-show)
+              if (dirName === 'else') {
+                continue;
+              }
+
+              dirDef = resolveAsset(options, 'directives', dirName, true);
+              if (dirDef) {
+                pushDir(dirName, dirDef);
+              }
+            }
+  }
+
+  /**
+   * Push a directive.
+   *
+   * @param {String} dirName
+   * @param {Object|Function} def
+   * @param {Array} [interpTokens]
+   */
+
+  function pushDir(dirName, def, interpTokens) {
+    var hasOneTimeToken = interpTokens && hasOneTime(interpTokens);
+    var parsed = !hasOneTimeToken && parseDirective(value);
+    dirs.push({
+      name: dirName,
+      attr: rawName,
+      raw: rawValue,
+      def: def,
+      arg: arg,
+      modifiers: modifiers,
+      // conversion from interpolation strings with one-time token
+      // to expression is differed until directive bind time so that we
+      // have access to the actual vm context for one-time bindings.
+      expression: parsed && parsed.expression,
+      filters: parsed && parsed.filters,
+      interp: interpTokens,
+      hasOneTime: hasOneTimeToken
+    });
+  }
+
+  if (dirs.length) {
+    return makeNodeLinkFn(dirs);
+  }
+}
+
+/**
+ * Parse modifiers from directive attribute name.
+ *
+ * @param {String} name
+ * @return {Object}
+ */
+
+function parseModifiers(name) {
+  var res = Object.create(null);
+  var match = name.match(modifierRE);
+  if (match) {
+    var i = match.length;
+    while (i--) {
+      res[match[i].slice(1)] = true;
+    }
+  }
+  return res;
+}
+
+/**
+ * Build a link function for all directives on a single node.
+ *
+ * @param {Array} directives
+ * @return {Function} directivesLinkFn
+ */
+
+function makeNodeLinkFn(directives) {
+  return function nodeLinkFn(vm, el, host, scope, frag) {
+    // reverse apply because it's sorted low to high
+    var i = directives.length;
+    while (i--) {
+      vm._bindDir(directives[i], el, host, scope, frag);
+    }
+  };
+}
+
+/**
+ * Check if an interpolation string contains one-time tokens.
+ *
+ * @param {Array} tokens
+ * @return {Boolean}
+ */
+
+function hasOneTime(tokens) {
+  var i = tokens.length;
+  while (i--) {
+    if (tokens[i].oneTime) return true;
+  }
+}
+
+function isScript(el) {
+  return el.tagName === 'SCRIPT' && (!el.hasAttribute('type') || el.getAttribute('type') === 'text/javascript');
+}
+
+var specialCharRE = /[^\w\-:\.]/;
+
+/**
+ * Process an element or a DocumentFragment based on a
+ * instance option object. This allows us to transclude
+ * a template node/fragment before the instance is created,
+ * so the processed fragment can then be cloned and reused
+ * in v-for.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Element|DocumentFragment}
+ */
+
+function transclude(el, options) {
+  // extract container attributes to pass them down
+  // to compiler, because they need to be compiled in
+  // parent scope. we are mutating the options object here
+  // assuming the same object will be used for compile
+  // right after this.
+  if (options) {
+    options._containerAttrs = extractAttrs(el);
+  }
+  // for template tags, what we want is its content as
+  // a documentFragment (for fragment instances)
+  if (isTemplate(el)) {
+    el = parseTemplate(el);
+  }
+  if (options) {
+    if (options._asComponent && !options.template) {
+      options.template = '<slot></slot>';
+    }
+    if (options.template) {
+      options._content = extractContent(el);
+      el = transcludeTemplate(el, options);
+    }
+  }
+  if (isFragment(el)) {
+    // anchors for fragment instance
+    // passing in `persist: true` to avoid them being
+    // discarded by IE during template cloning
+    prepend(createAnchor('v-start', true), el);
+    el.appendChild(createAnchor('v-end', true));
+  }
+  return el;
+}
+
+/**
+ * Process the template option.
+ * If the replace option is true this will swap the $el.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Element|DocumentFragment}
+ */
+
+function transcludeTemplate(el, options) {
+  var template = options.template;
+  var frag = parseTemplate(template, true);
+  if (frag) {
+    var replacer = frag.firstChild;
+    if (!replacer) {
+      return frag;
+    }
+    var tag = replacer.tagName && replacer.tagName.toLowerCase();
+    if (options.replace) {
+      /* istanbul ignore if */
+      if (el === document.body) {
+        'development' !== 'production' && warn('You are mounting an instance with a template to ' + '<body>. This will replace <body> entirely. You ' + 'should probably use `replace: false` here.');
+      }
+      // there are many cases where the instance must
+      // become a fragment instance: basically anything that
+      // can create more than 1 root nodes.
+      if (
+      // multi-children template
+      frag.childNodes.length > 1 ||
+      // non-element template
+      replacer.nodeType !== 1 ||
+      // single nested component
+      tag === 'component' || resolveAsset(options, 'components', tag) || hasBindAttr(replacer, 'is') ||
+      // element directive
+      resolveAsset(options, 'elementDirectives', tag) ||
+      // for block
+      replacer.hasAttribute('v-for') ||
+      // if block
+      replacer.hasAttribute('v-if')) {
+        return frag;
+      } else {
+        options._replacerAttrs = extractAttrs(replacer);
+        mergeAttrs(el, replacer);
+        return replacer;
+      }
+    } else {
+      el.appendChild(frag);
+      return el;
+    }
+  } else {
+    'development' !== 'production' && warn('Invalid template option: ' + template);
+  }
+}
+
+/**
+ * Helper to extract a component container's attributes
+ * into a plain object array.
+ *
+ * @param {Element} el
+ * @return {Array}
+ */
+
+function extractAttrs(el) {
+  if (el.nodeType === 1 && el.hasAttributes()) {
+    return toArray(el.attributes);
+  }
+}
+
+/**
+ * Merge the attributes of two elements, and make sure
+ * the class names are merged properly.
+ *
+ * @param {Element} from
+ * @param {Element} to
+ */
+
+function mergeAttrs(from, to) {
+  var attrs = from.attributes;
+  var i = attrs.length;
+  var name, value;
+  while (i--) {
+    name = attrs[i].name;
+    value = attrs[i].value;
+    if (!to.hasAttribute(name) && !specialCharRE.test(name)) {
+      to.setAttribute(name, value);
+    } else if (name === 'class' && !parseText(value) && (value = value.trim())) {
+      value.split(/\s+/).forEach(function (cls) {
+        addClass(to, cls);
+      });
+    }
+  }
+}
+
+/**
+ * Scan and determine slot content distribution.
+ * We do this during transclusion instead at compile time so that
+ * the distribution is decoupled from the compilation order of
+ * the slots.
+ *
+ * @param {Element|DocumentFragment} template
+ * @param {Element} content
+ * @param {Vue} vm
+ */
+
+function resolveSlots(vm, content) {
+  if (!content) {
+    return;
+  }
+  var contents = vm._slotContents = Object.create(null);
+  var el, name;
+  for (var i = 0, l = content.children.length; i < l; i++) {
+    el = content.children[i];
+    /* eslint-disable no-cond-assign */
+    if (name = el.getAttribute('slot')) {
+      (contents[name] || (contents[name] = [])).push(el);
+    }
+    /* eslint-enable no-cond-assign */
+    if ('development' !== 'production' && getBindAttr(el, 'slot')) {
+      warn('The "slot" attribute must be static.', vm.$parent);
+    }
+  }
+  for (name in contents) {
+    contents[name] = extractFragment(contents[name], content);
+  }
+  if (content.hasChildNodes()) {
+    var nodes = content.childNodes;
+    if (nodes.length === 1 && nodes[0].nodeType === 3 && !nodes[0].data.trim()) {
+      return;
+    }
+    contents['default'] = extractFragment(content.childNodes, content);
+  }
+}
+
+/**
+ * Extract qualified content nodes from a node list.
+ *
+ * @param {NodeList} nodes
+ * @return {DocumentFragment}
+ */
+
+function extractFragment(nodes, parent) {
+  var frag = document.createDocumentFragment();
+  nodes = toArray(nodes);
+  for (var i = 0, l = nodes.length; i < l; i++) {
+    var node = nodes[i];
+    if (isTemplate(node) && !node.hasAttribute('v-if') && !node.hasAttribute('v-for')) {
+      parent.removeChild(node);
+      node = parseTemplate(node, true);
+    }
+    frag.appendChild(node);
+  }
+  return frag;
+}
+
+
+
+var compiler = Object.freeze({
+	compile: compile,
+	compileAndLinkProps: compileAndLinkProps,
+	compileRoot: compileRoot,
+	transclude: transclude,
+	resolveSlots: resolveSlots
+});
+
+function stateMixin (Vue) {
+  /**
+   * Accessor for `$data` property, since setting $data
+   * requires observing the new object and updating
+   * proxied properties.
+   */
+
+  Object.defineProperty(Vue.prototype, '$data', {
+    get: function get() {
+      return this._data;
+    },
+    set: function set(newData) {
+      if (newData !== this._data) {
+        this._setData(newData);
+      }
+    }
+  });
+
+  /**
+   * Setup the scope of an instance, which contains:
+   * - observed data
+   * - computed properties
+   * - user methods
+   * - meta properties
+   */
+
+  Vue.prototype._initState = function () {
+    this._initProps();
+    this._initMeta();
+    this._initMethods();
+    this._initData();
+    this._initComputed();
+  };
+
+  /**
+   * Initialize props.
+   */
+
+  Vue.prototype._initProps = function () {
+    var options = this.$options;
+    var el = options.el;
+    var props = options.props;
+    if (props && !el) {
+      'development' !== 'production' && warn('Props will not be compiled if no `el` option is ' + 'provided at instantiation.', this);
+    }
+    // make sure to convert string selectors into element now
+    el = options.el = query(el);
+    this._propsUnlinkFn = el && el.nodeType === 1 && props
+    // props must be linked in proper scope if inside v-for
+    ? compileAndLinkProps(this, el, props, this._scope) : null;
+  };
+
+  /**
+   * Initialize the data.
+   */
+
+  Vue.prototype._initData = function () {
+    var dataFn = this.$options.data;
+    var data = this._data = dataFn ? dataFn() : {};
+    if (!isPlainObject(data)) {
+      data = {};
+      'development' !== 'production' && warn('data functions should return an object.', this);
+    }
+    var props = this._props;
+    // proxy data on instance
+    var keys = Object.keys(data);
+    var i, key;
+    i = keys.length;
+    while (i--) {
+      key = keys[i];
+      // there are two scenarios where we can proxy a data key:
+      // 1. it's not already defined as a prop
+      // 2. it's provided via a instantiation option AND there are no
+      //    template prop present
+      if (!props || !hasOwn(props, key)) {
+        this._proxy(key);
+      } else if ('development' !== 'production') {
+        warn('Data field "' + key + '" is already defined ' + 'as a prop. To provide default value for a prop, use the "default" ' + 'prop option; if you want to pass prop values to an instantiation ' + 'call, use the "propsData" option.', this);
+      }
+    }
+    // observe data
+    observe(data, this);
+  };
+
+  /**
+   * Swap the instance's $data. Called in $data's setter.
+   *
+   * @param {Object} newData
+   */
+
+  Vue.prototype._setData = function (newData) {
+    newData = newData || {};
+    var oldData = this._data;
+    this._data = newData;
+    var keys, key, i;
+    // unproxy keys not present in new data
+    keys = Object.keys(oldData);
+    i = keys.length;
+    while (i--) {
+      key = keys[i];
+      if (!(key in newData)) {
+        this._unproxy(key);
+      }
+    }
+    // proxy keys not already proxied,
+    // and trigger change for changed values
+    keys = Object.keys(newData);
+    i = keys.length;
+    while (i--) {
+      key = keys[i];
+      if (!hasOwn(this, key)) {
+        // new property
+        this._proxy(key);
+      }
+    }
+    oldData.__ob__.removeVm(this);
+    observe(newData, this);
+    this._digest();
+  };
+
+  /**
+   * Proxy a property, so that
+   * vm.prop === vm._data.prop
+   *
+   * @param {String} key
+   */
+
+  Vue.prototype._proxy = function (key) {
+    if (!isReserved(key)) {
+      // need to store ref to self here
+      // because these getter/setters might
+      // be called by child scopes via
+      // prototype inheritance.
+      var self = this;
+      Object.defineProperty(self, key, {
+        configurable: true,
+        enumerable: true,
+        get: function proxyGetter() {
+          return self._data[key];
+        },
+        set: function proxySetter(val) {
+          self._data[key] = val;
+        }
+      });
+    }
+  };
+
+  /**
+   * Unproxy a property.
+   *
+   * @param {String} key
+   */
+
+  Vue.prototype._unproxy = function (key) {
+    if (!isReserved(key)) {
+      delete this[key];
+    }
+  };
+
+  /**
+   * Force update on every watcher in scope.
+   */
+
+  Vue.prototype._digest = function () {
+    for (var i = 0, l = this._watchers.length; i < l; i++) {
+      this._watchers[i].update(true); // shallow updates
+    }
+  };
+
+  /**
+   * Setup computed properties. They are essentially
+   * special getter/setters
+   */
+
+  function noop() {}
+  Vue.prototype._initComputed = function () {
+    var computed = this.$options.computed;
+    if (computed) {
+      for (var key in computed) {
+        var userDef = computed[key];
+        var def = {
+          enumerable: true,
+          configurable: true
+        };
+        if (typeof userDef === 'function') {
+          def.get = makeComputedGetter(userDef, this);
+          def.set = noop;
+        } else {
+          def.get = userDef.get ? userDef.cache !== false ? makeComputedGetter(userDef.get, this) : bind(userDef.get, this) : noop;
+          def.set = userDef.set ? bind(userDef.set, this) : noop;
+        }
+        Object.defineProperty(this, key, def);
+      }
+    }
+  };
+
+  function makeComputedGetter(getter, owner) {
+    var watcher = new Watcher(owner, getter, null, {
+      lazy: true
+    });
+    return function computedGetter() {
+      if (watcher.dirty) {
+        watcher.evaluate();
+      }
+      if (Dep.target) {
+        watcher.depend();
+      }
+      return watcher.value;
+    };
+  }
+
+  /**
+   * Setup instance methods. Methods must be bound to the
+   * instance since they might be passed down as a prop to
+   * child components.
+   */
+
+  Vue.prototype._initMethods = function () {
+    var methods = this.$options.methods;
+    if (methods) {
+      for (var key in methods) {
+        this[key] = bind(methods[key], this);
+      }
+    }
+  };
+
+  /**
+   * Initialize meta information like $index, $key & $value.
+   */
+
+  Vue.prototype._initMeta = function () {
+    var metas = this.$options._meta;
+    if (metas) {
+      for (var key in metas) {
+        defineReactive(this, key, metas[key]);
+      }
+    }
+  };
+}
+
+var eventRE = /^v-on:|^@/;
+
+function eventsMixin (Vue) {
+  /**
+   * Setup the instance's option events & watchers.
+   * If the value is a string, we pull it from the
+   * instance's methods by name.
+   */
+
+  Vue.prototype._initEvents = function () {
+    var options = this.$options;
+    if (options._asComponent) {
+      registerComponentEvents(this, options.el);
+    }
+    registerCallbacks(this, '$on', options.events);
+    registerCallbacks(this, '$watch', options.watch);
+  };
+
+  /**
+   * Register v-on events on a child component
+   *
+   * @param {Vue} vm
+   * @param {Element} el
+   */
+
+  function registerComponentEvents(vm, el) {
+    var attrs = el.attributes;
+    var name, value, handler;
+    for (var i = 0, l = attrs.length; i < l; i++) {
+      name = attrs[i].name;
+      if (eventRE.test(name)) {
+        name = name.replace(eventRE, '');
+        // force the expression into a statement so that
+        // it always dynamically resolves the method to call (#2670)
+        // kinda ugly hack, but does the job.
+        value = attrs[i].value;
+        if (isSimplePath(value)) {
+          value += '.apply(this, $arguments)';
+        }
+        handler = (vm._scope || vm._context).$eval(value, true);
+        handler._fromParent = true;
+        vm.$on(name.replace(eventRE), handler);
+      }
+    }
+  }
+
+  /**
+   * Register callbacks for option events and watchers.
+   *
+   * @param {Vue} vm
+   * @param {String} action
+   * @param {Object} hash
+   */
+
+  function registerCallbacks(vm, action, hash) {
+    if (!hash) return;
+    var handlers, key, i, j;
+    for (key in hash) {
+      handlers = hash[key];
+      if (isArray(handlers)) {
+        for (i = 0, j = handlers.length; i < j; i++) {
+          register(vm, action, key, handlers[i]);
+        }
+      } else {
+        register(vm, action, key, handlers);
+      }
+    }
+  }
+
+  /**
+   * Helper to register an event/watch callback.
+   *
+   * @param {Vue} vm
+   * @param {String} action
+   * @param {String} key
+   * @param {Function|String|Object} handler
+   * @param {Object} [options]
+   */
+
+  function register(vm, action, key, handler, options) {
+    var type = typeof handler;
+    if (type === 'function') {
+      vm[action](key, handler, options);
+    } else if (type === 'string') {
+      var methods = vm.$options.methods;
+      var method = methods && methods[handler];
+      if (method) {
+        vm[action](key, method, options);
+      } else {
+        'development' !== 'production' && warn('Unknown method: "' + handler + '" when ' + 'registering callback for ' + action + ': "' + key + '".', vm);
+      }
+    } else if (handler && type === 'object') {
+      register(vm, action, key, handler.handler, handler);
+    }
+  }
+
+  /**
+   * Setup recursive attached/detached calls
+   */
+
+  Vue.prototype._initDOMHooks = function () {
+    this.$on('hook:attached', onAttached);
+    this.$on('hook:detached', onDetached);
+  };
+
+  /**
+   * Callback to recursively call attached hook on children
+   */
+
+  function onAttached() {
+    if (!this._isAttached) {
+      this._isAttached = true;
+      this.$children.forEach(callAttach);
+    }
+  }
+
+  /**
+   * Iterator to call attached hook
+   *
+   * @param {Vue} child
+   */
+
+  function callAttach(child) {
+    if (!child._isAttached && inDoc(child.$el)) {
+      child._callHook('attached');
+    }
+  }
+
+  /**
+   * Callback to recursively call detached hook on children
+   */
+
+  function onDetached() {
+    if (this._isAttached) {
+      this._isAttached = false;
+      this.$children.forEach(callDetach);
+    }
+  }
+
+  /**
+   * Iterator to call detached hook
+   *
+   * @param {Vue} child
+   */
+
+  function callDetach(child) {
+    if (child._isAttached && !inDoc(child.$el)) {
+      child._callHook('detached');
+    }
+  }
+
+  /**
+   * Trigger all handlers for a hook
+   *
+   * @param {String} hook
+   */
+
+  Vue.prototype._callHook = function (hook) {
+    this.$emit('pre-hook:' + hook);
+    var handlers = this.$options[hook];
+    if (handlers) {
+      for (var i = 0, j = handlers.length; i < j; i++) {
+        handlers[i].call(this);
+      }
+    }
+    this.$emit('hook:' + hook);
+  };
+}
+
+function noop$1() {}
+
+/**
+ * A directive links a DOM element with a piece of data,
+ * which is the result of evaluating an expression.
+ * It registers a watcher with the expression and calls
+ * the DOM update function when a change is triggered.
+ *
+ * @param {Object} descriptor
+ *                 - {String} name
+ *                 - {Object} def
+ *                 - {String} expression
+ *                 - {Array<Object>} [filters]
+ *                 - {Object} [modifiers]
+ *                 - {Boolean} literal
+ *                 - {String} attr
+ *                 - {String} arg
+ *                 - {String} raw
+ *                 - {String} [ref]
+ *                 - {Array<Object>} [interp]
+ *                 - {Boolean} [hasOneTime]
+ * @param {Vue} vm
+ * @param {Node} el
+ * @param {Vue} [host] - transclusion host component
+ * @param {Object} [scope] - v-for scope
+ * @param {Fragment} [frag] - owner fragment
+ * @constructor
+ */
+function Directive(descriptor, vm, el, host, scope, frag) {
+  this.vm = vm;
+  this.el = el;
+  // copy descriptor properties
+  this.descriptor = descriptor;
+  this.name = descriptor.name;
+  this.expression = descriptor.expression;
+  this.arg = descriptor.arg;
+  this.modifiers = descriptor.modifiers;
+  this.filters = descriptor.filters;
+  this.literal = this.modifiers && this.modifiers.literal;
+  // private
+  this._locked = false;
+  this._bound = false;
+  this._listeners = null;
+  // link context
+  this._host = host;
+  this._scope = scope;
+  this._frag = frag;
+  // store directives on node in dev mode
+  if ('development' !== 'production' && this.el) {
+    this.el._vue_directives = this.el._vue_directives || [];
+    this.el._vue_directives.push(this);
+  }
+}
+
+/**
+ * Initialize the directive, mixin definition properties,
+ * setup the watcher, call definition bind() and update()
+ * if present.
+ */
+
+Directive.prototype._bind = function () {
+  var name = this.name;
+  var descriptor = this.descriptor;
+
+  // remove attribute
+  if ((name !== 'cloak' || this.vm._isCompiled) && this.el && this.el.removeAttribute) {
+    var attr = descriptor.attr || 'v-' + name;
+    this.el.removeAttribute(attr);
+  }
+
+  // copy def properties
+  var def = descriptor.def;
+  if (typeof def === 'function') {
+    this.update = def;
+  } else {
+    extend(this, def);
+  }
+
+  // setup directive params
+  this._setupParams();
+
+  // initial bind
+  if (this.bind) {
+    this.bind();
+  }
+  this._bound = true;
+
+  if (this.literal) {
+    this.update && this.update(descriptor.raw);
+  } else if ((this.expression || this.modifiers) && (this.update || this.twoWay) && !this._checkStatement()) {
+    // wrapped updater for context
+    var dir = this;
+    if (this.update) {
+      this._update = function (val, oldVal) {
+        if (!dir._locked) {
+          dir.update(val, oldVal);
+        }
+      };
+    } else {
+      this._update = noop$1;
+    }
+    var preProcess = this._preProcess ? bind(this._preProcess, this) : null;
+    var postProcess = this._postProcess ? bind(this._postProcess, this) : null;
+    var watcher = this._watcher = new Watcher(this.vm, this.expression, this._update, // callback
+    {
+      filters: this.filters,
+      twoWay: this.twoWay,
+      deep: this.deep,
+      preProcess: preProcess,
+      postProcess: postProcess,
+      scope: this._scope
+    });
+    // v-model with inital inline value need to sync back to
+    // model instead of update to DOM on init. They would
+    // set the afterBind hook to indicate that.
+    if (this.afterBind) {
+      this.afterBind();
+    } else if (this.update) {
+      this.update(watcher.value);
+    }
+  }
+};
+
+/**
+ * Setup all param attributes, e.g. track-by,
+ * transition-mode, etc...
+ */
+
+Directive.prototype._setupParams = function () {
+  if (!this.params) {
+    return;
+  }
+  var params = this.params;
+  // swap the params array with a fresh object.
+  this.params = Object.create(null);
+  var i = params.length;
+  var key, val, mappedKey;
+  while (i--) {
+    key = hyphenate(params[i]);
+    mappedKey = camelize(key);
+    val = getBindAttr(this.el, key);
+    if (val != null) {
+      // dynamic
+      this._setupParamWatcher(mappedKey, val);
+    } else {
+      // static
+      val = getAttr(this.el, key);
+      if (val != null) {
+        this.params[mappedKey] = val === '' ? true : val;
+      }
+    }
+  }
+};
+
+/**
+ * Setup a watcher for a dynamic param.
+ *
+ * @param {String} key
+ * @param {String} expression
+ */
+
+Directive.prototype._setupParamWatcher = function (key, expression) {
+  var self = this;
+  var called = false;
+  var unwatch = (this._scope || this.vm).$watch(expression, function (val, oldVal) {
+    self.params[key] = val;
+    // since we are in immediate mode,
+    // only call the param change callbacks if this is not the first update.
+    if (called) {
+      var cb = self.paramWatchers && self.paramWatchers[key];
+      if (cb) {
+        cb.call(self, val, oldVal);
+      }
+    } else {
+      called = true;
+    }
+  }, {
+    immediate: true,
+    user: false
+  });(this._paramUnwatchFns || (this._paramUnwatchFns = [])).push(unwatch);
+};
+
+/**
+ * Check if the directive is a function caller
+ * and if the expression is a callable one. If both true,
+ * we wrap up the expression and use it as the event
+ * handler.
+ *
+ * e.g. on-click="a++"
+ *
+ * @return {Boolean}
+ */
+
+Directive.prototype._checkStatement = function () {
+  var expression = this.expression;
+  if (expression && this.acceptStatement && !isSimplePath(expression)) {
+    var fn = parseExpression$1(expression).get;
+    var scope = this._scope || this.vm;
+    var handler = function handler(e) {
+      scope.$event = e;
+      fn.call(scope, scope);
+      scope.$event = null;
+    };
+    if (this.filters) {
+      handler = scope._applyFilters(handler, null, this.filters);
+    }
+    this.update(handler);
+    return true;
+  }
+};
+
+/**
+ * Set the corresponding value with the setter.
+ * This should only be used in two-way directives
+ * e.g. v-model.
+ *
+ * @param {*} value
+ * @public
+ */
+
+Directive.prototype.set = function (value) {
+  /* istanbul ignore else */
+  if (this.twoWay) {
+    this._withLock(function () {
+      this._watcher.set(value);
+    });
+  } else if ('development' !== 'production') {
+    warn('Directive.set() can only be used inside twoWay' + 'directives.');
+  }
+};
+
+/**
+ * Execute a function while preventing that function from
+ * triggering updates on this directive instance.
+ *
+ * @param {Function} fn
+ */
+
+Directive.prototype._withLock = function (fn) {
+  var self = this;
+  self._locked = true;
+  fn.call(self);
+  nextTick(function () {
+    self._locked = false;
+  });
+};
+
+/**
+ * Convenience method that attaches a DOM event listener
+ * to the directive element and autometically tears it down
+ * during unbind.
+ *
+ * @param {String} event
+ * @param {Function} handler
+ * @param {Boolean} [useCapture]
+ */
+
+Directive.prototype.on = function (event, handler, useCapture) {
+  on(this.el, event, handler, useCapture);(this._listeners || (this._listeners = [])).push([event, handler]);
+};
+
+/**
+ * Teardown the watcher and call unbind.
+ */
+
+Directive.prototype._teardown = function () {
+  if (this._bound) {
+    this._bound = false;
+    if (this.unbind) {
+      this.unbind();
+    }
+    if (this._watcher) {
+      this._watcher.teardown();
+    }
+    var listeners = this._listeners;
+    var i;
+    if (listeners) {
+      i = listeners.length;
+      while (i--) {
+        off(this.el, listeners[i][0], listeners[i][1]);
+      }
+    }
+    var unwatchFns = this._paramUnwatchFns;
+    if (unwatchFns) {
+      i = unwatchFns.length;
+      while (i--) {
+        unwatchFns[i]();
+      }
+    }
+    if ('development' !== 'production' && this.el) {
+      this.el._vue_directives.$remove(this);
+    }
+    this.vm = this.el = this._watcher = this._listeners = null;
+  }
+};
+
+function lifecycleMixin (Vue) {
+  /**
+   * Update v-ref for component.
+   *
+   * @param {Boolean} remove
+   */
+
+  Vue.prototype._updateRef = function (remove) {
+    var ref = this.$options._ref;
+    if (ref) {
+      var refs = (this._scope || this._context).$refs;
+      if (remove) {
+        if (refs[ref] === this) {
+          refs[ref] = null;
+        }
+      } else {
+        refs[ref] = this;
+      }
+    }
+  };
+
+  /**
+   * Transclude, compile and link element.
+   *
+   * If a pre-compiled linker is available, that means the
+   * passed in element will be pre-transcluded and compiled
+   * as well - all we need to do is to call the linker.
+   *
+   * Otherwise we need to call transclude/compile/link here.
+   *
+   * @param {Element} el
+   */
+
+  Vue.prototype._compile = function (el) {
+    var options = this.$options;
+
+    // transclude and init element
+    // transclude can potentially replace original
+    // so we need to keep reference; this step also injects
+    // the template and caches the original attributes
+    // on the container node and replacer node.
+    var original = el;
+    el = transclude(el, options);
+    this._initElement(el);
+
+    // handle v-pre on root node (#2026)
+    if (el.nodeType === 1 && getAttr(el, 'v-pre') !== null) {
+      return;
+    }
+
+    // root is always compiled per-instance, because
+    // container attrs and props can be different every time.
+    var contextOptions = this._context && this._context.$options;
+    var rootLinker = compileRoot(el, options, contextOptions);
+
+    // resolve slot distribution
+    resolveSlots(this, options._content);
+
+    // compile and link the rest
+    var contentLinkFn;
+    var ctor = this.constructor;
+    // component compilation can be cached
+    // as long as it's not using inline-template
+    if (options._linkerCachable) {
+      contentLinkFn = ctor.linker;
+      if (!contentLinkFn) {
+        contentLinkFn = ctor.linker = compile(el, options);
+      }
+    }
+
+    // link phase
+    // make sure to link root with prop scope!
+    var rootUnlinkFn = rootLinker(this, el, this._scope);
+    var contentUnlinkFn = contentLinkFn ? contentLinkFn(this, el) : compile(el, options)(this, el);
+
+    // register composite unlink function
+    // to be called during instance destruction
+    this._unlinkFn = function () {
+      rootUnlinkFn();
+      // passing destroying: true to avoid searching and
+      // splicing the directives
+      contentUnlinkFn(true);
+    };
+
+    // finally replace original
+    if (options.replace) {
+      replace(original, el);
+    }
+
+    this._isCompiled = true;
+    this._callHook('compiled');
+  };
+
+  /**
+   * Initialize instance element. Called in the public
+   * $mount() method.
+   *
+   * @param {Element} el
+   */
+
+  Vue.prototype._initElement = function (el) {
+    if (isFragment(el)) {
+      this._isFragment = true;
+      this.$el = this._fragmentStart = el.firstChild;
+      this._fragmentEnd = el.lastChild;
+      // set persisted text anchors to empty
+      if (this._fragmentStart.nodeType === 3) {
+        this._fragmentStart.data = this._fragmentEnd.data = '';
+      }
+      this._fragment = el;
+    } else {
+      this.$el = el;
+    }
+    this.$el.__vue__ = this;
+    this._callHook('beforeCompile');
+  };
+
+  /**
+   * Create and bind a directive to an element.
+   *
+   * @param {Object} descriptor - parsed directive descriptor
+   * @param {Node} node   - target node
+   * @param {Vue} [host] - transclusion host component
+   * @param {Object} [scope] - v-for scope
+   * @param {Fragment} [frag] - owner fragment
+   */
+
+  Vue.prototype._bindDir = function (descriptor, node, host, scope, frag) {
+    this._directives.push(new Directive(descriptor, this, node, host, scope, frag));
+  };
+
+  /**
+   * Teardown an instance, unobserves the data, unbind all the
+   * directives, turn off all the event listeners, etc.
+   *
+   * @param {Boolean} remove - whether to remove the DOM node.
+   * @param {Boolean} deferCleanup - if true, defer cleanup to
+   *                                 be called later
+   */
+
+  Vue.prototype._destroy = function (remove, deferCleanup) {
+    if (this._isBeingDestroyed) {
+      if (!deferCleanup) {
+        this._cleanup();
+      }
+      return;
+    }
+
+    var destroyReady;
+    var pendingRemoval;
+
+    var self = this;
+    // Cleanup should be called either synchronously or asynchronoysly as
+    // callback of this.$remove(), or if remove and deferCleanup are false.
+    // In any case it should be called after all other removing, unbinding and
+    // turning of is done
+    var cleanupIfPossible = function cleanupIfPossible() {
+      if (destroyReady && !pendingRemoval && !deferCleanup) {
+        self._cleanup();
+      }
+    };
+
+    // remove DOM element
+    if (remove && this.$el) {
+      pendingRemoval = true;
+      this.$remove(function () {
+        pendingRemoval = false;
+        cleanupIfPossible();
+      });
+    }
+
+    this._callHook('beforeDestroy');
+    this._isBeingDestroyed = true;
+    var i;
+    // remove self from parent. only necessary
+    // if parent is not being destroyed as well.
+    var parent = this.$parent;
+    if (parent && !parent._isBeingDestroyed) {
+      parent.$children.$remove(this);
+      // unregister ref (remove: true)
+      this._updateRef(true);
+    }
+    // destroy all children.
+    i = this.$children.length;
+    while (i--) {
+      this.$children[i].$destroy();
+    }
+    // teardown props
+    if (this._propsUnlinkFn) {
+      this._propsUnlinkFn();
+    }
+    // teardown all directives. this also tearsdown all
+    // directive-owned watchers.
+    if (this._unlinkFn) {
+      this._unlinkFn();
+    }
+    i = this._watchers.length;
+    while (i--) {
+      this._watchers[i].teardown();
+    }
+    // remove reference to self on $el
+    if (this.$el) {
+      this.$el.__vue__ = null;
+    }
+
+    destroyReady = true;
+    cleanupIfPossible();
+  };
+
+  /**
+   * Clean up to ensure garbage collection.
+   * This is called after the leave transition if there
+   * is any.
+   */
+
+  Vue.prototype._cleanup = function () {
+    if (this._isDestroyed) {
+      return;
+    }
+    // remove self from owner fragment
+    // do it in cleanup so that we can call $destroy with
+    // defer right when a fragment is about to be removed.
+    if (this._frag) {
+      this._frag.children.$remove(this);
+    }
+    // remove reference from data ob
+    // frozen object may not have observer.
+    if (this._data && this._data.__ob__) {
+      this._data.__ob__.removeVm(this);
+    }
+    // Clean up references to private properties and other
+    // instances. preserve reference to _data so that proxy
+    // accessors still work. The only potential side effect
+    // here is that mutating the instance after it's destroyed
+    // may affect the state of other components that are still
+    // observing the same object, but that seems to be a
+    // reasonable responsibility for the user rather than
+    // always throwing an error on them.
+    this.$el = this.$parent = this.$root = this.$children = this._watchers = this._context = this._scope = this._directives = null;
+    // call the last hook...
+    this._isDestroyed = true;
+    this._callHook('destroyed');
+    // turn off all instance listeners.
+    this.$off();
+  };
+}
+
+function miscMixin (Vue) {
+  /**
+   * Apply a list of filter (descriptors) to a value.
+   * Using plain for loops here because this will be called in
+   * the getter of any watcher with filters so it is very
+   * performance sensitive.
+   *
+   * @param {*} value
+   * @param {*} [oldValue]
+   * @param {Array} filters
+   * @param {Boolean} write
+   * @return {*}
+   */
+
+  Vue.prototype._applyFilters = function (value, oldValue, filters, write) {
+    var filter, fn, args, arg, offset, i, l, j, k;
+    for (i = 0, l = filters.length; i < l; i++) {
+      filter = filters[write ? l - i - 1 : i];
+      fn = resolveAsset(this.$options, 'filters', filter.name, true);
+      if (!fn) continue;
+      fn = write ? fn.write : fn.read || fn;
+      if (typeof fn !== 'function') continue;
+      args = write ? [value, oldValue] : [value];
+      offset = write ? 2 : 1;
+      if (filter.args) {
+        for (j = 0, k = filter.args.length; j < k; j++) {
+          arg = filter.args[j];
+          args[j + offset] = arg.dynamic ? this.$get(arg.value) : arg.value;
+        }
+      }
+      value = fn.apply(this, args);
+    }
+    return value;
+  };
+
+  /**
+   * Resolve a component, depending on whether the component
+   * is defined normally or using an async factory function.
+   * Resolves synchronously if already resolved, otherwise
+   * resolves asynchronously and caches the resolved
+   * constructor on the factory.
+   *
+   * @param {String|Function} value
+   * @param {Function} cb
+   */
+
+  Vue.prototype._resolveComponent = function (value, cb) {
+    var factory;
+    if (typeof value === 'function') {
+      factory = value;
+    } else {
+      factory = resolveAsset(this.$options, 'components', value, true);
+    }
+    /* istanbul ignore if */
+    if (!factory) {
+      return;
+    }
+    // async component factory
+    if (!factory.options) {
+      if (factory.resolved) {
+        // cached
+        cb(factory.resolved);
+      } else if (factory.requested) {
+        // pool callbacks
+        factory.pendingCallbacks.push(cb);
+      } else {
+        factory.requested = true;
+        var cbs = factory.pendingCallbacks = [cb];
+        factory.call(this, function resolve(res) {
+          if (isPlainObject(res)) {
+            res = Vue.extend(res);
+          }
+          // cache resolved
+          factory.resolved = res;
+          // invoke callbacks
+          for (var i = 0, l = cbs.length; i < l; i++) {
+            cbs[i](res);
+          }
+        }, function reject(reason) {
+          'development' !== 'production' && warn('Failed to resolve async component' + (typeof value === 'string' ? ': ' + value : '') + '. ' + (reason ? '\nReason: ' + reason : ''));
+        });
+      }
+    } else {
+      // normal component
+      cb(factory);
+    }
+  };
+}
+
+var filterRE$1 = /[^|]\|[^|]/;
+
+function dataAPI (Vue) {
+  /**
+   * Get the value from an expression on this vm.
+   *
+   * @param {String} exp
+   * @param {Boolean} [asStatement]
+   * @return {*}
+   */
+
+  Vue.prototype.$get = function (exp, asStatement) {
+    var res = parseExpression$1(exp);
+    if (res) {
+      if (asStatement) {
+        var self = this;
+        return function statementHandler() {
+          self.$arguments = toArray(arguments);
+          var result = res.get.call(self, self);
+          self.$arguments = null;
+          return result;
+        };
+      } else {
+        try {
+          return res.get.call(this, this);
+        } catch (e) {}
+      }
+    }
+  };
+
+  /**
+   * Set the value from an expression on this vm.
+   * The expression must be a valid left-hand
+   * expression in an assignment.
+   *
+   * @param {String} exp
+   * @param {*} val
+   */
+
+  Vue.prototype.$set = function (exp, val) {
+    var res = parseExpression$1(exp, true);
+    if (res && res.set) {
+      res.set.call(this, this, val);
+    }
+  };
+
+  /**
+   * Delete a property on the VM
+   *
+   * @param {String} key
+   */
+
+  Vue.prototype.$delete = function (key) {
+    del(this._data, key);
+  };
+
+  /**
+   * Watch an expression, trigger callback when its
+   * value changes.
+   *
+   * @param {String|Function} expOrFn
+   * @param {Function} cb
+   * @param {Object} [options]
+   *                 - {Boolean} deep
+   *                 - {Boolean} immediate
+   * @return {Function} - unwatchFn
+   */
+
+  Vue.prototype.$watch = function (expOrFn, cb, options) {
+    var vm = this;
+    var parsed;
+    if (typeof expOrFn === 'string') {
+      parsed = parseDirective(expOrFn);
+      expOrFn = parsed.expression;
+    }
+    var watcher = new Watcher(vm, expOrFn, cb, {
+      deep: options && options.deep,
+      sync: options && options.sync,
+      filters: parsed && parsed.filters,
+      user: !options || options.user !== false
+    });
+    if (options && options.immediate) {
+      cb.call(vm, watcher.value);
+    }
+    return function unwatchFn() {
+      watcher.teardown();
+    };
+  };
+
+  /**
+   * Evaluate a text directive, including filters.
+   *
+   * @param {String} text
+   * @param {Boolean} [asStatement]
+   * @return {String}
+   */
+
+  Vue.prototype.$eval = function (text, asStatement) {
+    // check for filters.
+    if (filterRE$1.test(text)) {
+      var dir = parseDirective(text);
+      // the filter regex check might give false positive
+      // for pipes inside strings, so it's possible that
+      // we don't get any filters here
+      var val = this.$get(dir.expression, asStatement);
+      return dir.filters ? this._applyFilters(val, null, dir.filters) : val;
+    } else {
+      // no filter
+      return this.$get(text, asStatement);
+    }
+  };
+
+  /**
+   * Interpolate a piece of template text.
+   *
+   * @param {String} text
+   * @return {String}
+   */
+
+  Vue.prototype.$interpolate = function (text) {
+    var tokens = parseText(text);
+    var vm = this;
+    if (tokens) {
+      if (tokens.length === 1) {
+        return vm.$eval(tokens[0].value) + '';
+      } else {
+        return tokens.map(function (token) {
+          return token.tag ? vm.$eval(token.value) : token.value;
+        }).join('');
+      }
+    } else {
+      return text;
+    }
+  };
+
+  /**
+   * Log instance data as a plain JS object
+   * so that it is easier to inspect in console.
+   * This method assumes console is available.
+   *
+   * @param {String} [path]
+   */
+
+  Vue.prototype.$log = function (path) {
+    var data = path ? getPath(this._data, path) : this._data;
+    if (data) {
+      data = clean(data);
+    }
+    // include computed fields
+    if (!path) {
+      var key;
+      for (key in this.$options.computed) {
+        data[key] = clean(this[key]);
+      }
+      if (this._props) {
+        for (key in this._props) {
+          data[key] = clean(this[key]);
+        }
+      }
+    }
+    console.log(data);
+  };
+
+  /**
+   * "clean" a getter/setter converted object into a plain
+   * object copy.
+   *
+   * @param {Object} - obj
+   * @return {Object}
+   */
+
+  function clean(obj) {
+    return JSON.parse(JSON.stringify(obj));
+  }
+}
+
+function domAPI (Vue) {
+  /**
+   * Convenience on-instance nextTick. The callback is
+   * auto-bound to the instance, and this avoids component
+   * modules having to rely on the global Vue.
+   *
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$nextTick = function (fn) {
+    nextTick(fn, this);
+  };
+
+  /**
+   * Append instance to target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$appendTo = function (target, cb, withTransition) {
+    return insert(this, target, cb, withTransition, append, appendWithTransition);
+  };
+
+  /**
+   * Prepend instance to target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$prependTo = function (target, cb, withTransition) {
+    target = query(target);
+    if (target.hasChildNodes()) {
+      this.$before(target.firstChild, cb, withTransition);
+    } else {
+      this.$appendTo(target, cb, withTransition);
+    }
+    return this;
+  };
+
+  /**
+   * Insert instance before target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$before = function (target, cb, withTransition) {
+    return insert(this, target, cb, withTransition, beforeWithCb, beforeWithTransition);
+  };
+
+  /**
+   * Insert instance after target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$after = function (target, cb, withTransition) {
+    target = query(target);
+    if (target.nextSibling) {
+      this.$before(target.nextSibling, cb, withTransition);
+    } else {
+      this.$appendTo(target.parentNode, cb, withTransition);
+    }
+    return this;
+  };
+
+  /**
+   * Remove instance from DOM
+   *
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$remove = function (cb, withTransition) {
+    if (!this.$el.parentNode) {
+      return cb && cb();
+    }
+    var inDocument = this._isAttached && inDoc(this.$el);
+    // if we are not in document, no need to check
+    // for transitions
+    if (!inDocument) withTransition = false;
+    var self = this;
+    var realCb = function realCb() {
+      if (inDocument) self._callHook('detached');
+      if (cb) cb();
+    };
+    if (this._isFragment) {
+      removeNodeRange(this._fragmentStart, this._fragmentEnd, this, this._fragment, realCb);
+    } else {
+      var op = withTransition === false ? removeWithCb : removeWithTransition;
+      op(this.$el, this, realCb);
+    }
+    return this;
+  };
+
+  /**
+   * Shared DOM insertion function.
+   *
+   * @param {Vue} vm
+   * @param {Element} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition]
+   * @param {Function} op1 - op for non-transition insert
+   * @param {Function} op2 - op for transition insert
+   * @return vm
+   */
+
+  function insert(vm, target, cb, withTransition, op1, op2) {
+    target = query(target);
+    var targetIsDetached = !inDoc(target);
+    var op = withTransition === false || targetIsDetached ? op1 : op2;
+    var shouldCallHook = !targetIsDetached && !vm._isAttached && !inDoc(vm.$el);
+    if (vm._isFragment) {
+      mapNodeRange(vm._fragmentStart, vm._fragmentEnd, function (node) {
+        op(node, target, vm);
+      });
+      cb && cb();
+    } else {
+      op(vm.$el, target, vm, cb);
+    }
+    if (shouldCallHook) {
+      vm._callHook('attached');
+    }
+    return vm;
+  }
+
+  /**
+   * Check for selectors
+   *
+   * @param {String|Element} el
+   */
+
+  function query(el) {
+    return typeof el === 'string' ? document.querySelector(el) : el;
+  }
+
+  /**
+   * Append operation that takes a callback.
+   *
+   * @param {Node} el
+   * @param {Node} target
+   * @param {Vue} vm - unused
+   * @param {Function} [cb]
+   */
+
+  function append(el, target, vm, cb) {
+    target.appendChild(el);
+    if (cb) cb();
+  }
+
+  /**
+   * InsertBefore operation that takes a callback.
+   *
+   * @param {Node} el
+   * @param {Node} target
+   * @param {Vue} vm - unused
+   * @param {Function} [cb]
+   */
+
+  function beforeWithCb(el, target, vm, cb) {
+    before(el, target);
+    if (cb) cb();
+  }
+
+  /**
+   * Remove operation that takes a callback.
+   *
+   * @param {Node} el
+   * @param {Vue} vm - unused
+   * @param {Function} [cb]
+   */
+
+  function removeWithCb(el, vm, cb) {
+    remove(el);
+    if (cb) cb();
+  }
+}
+
+function eventsAPI (Vue) {
+  /**
+   * Listen on the given `event` with `fn`.
+   *
+   * @param {String} event
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$on = function (event, fn) {
+    (this._events[event] || (this._events[event] = [])).push(fn);
+    modifyListenerCount(this, event, 1);
+    return this;
+  };
+
+  /**
+   * Adds an `event` listener that will be invoked a single
+   * time then automatically removed.
+   *
+   * @param {String} event
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$once = function (event, fn) {
+    var self = this;
+    function on() {
+      self.$off(event, on);
+      fn.apply(this, arguments);
+    }
+    on.fn = fn;
+    this.$on(event, on);
+    return this;
+  };
+
+  /**
+   * Remove the given callback for `event` or all
+   * registered callbacks.
+   *
+   * @param {String} event
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$off = function (event, fn) {
+    var cbs;
+    // all
+    if (!arguments.length) {
+      if (this.$parent) {
+        for (event in this._events) {
+          cbs = this._events[event];
+          if (cbs) {
+            modifyListenerCount(this, event, -cbs.length);
+          }
+        }
+      }
+      this._events = {};
+      return this;
+    }
+    // specific event
+    cbs = this._events[event];
+    if (!cbs) {
+      return this;
+    }
+    if (arguments.length === 1) {
+      modifyListenerCount(this, event, -cbs.length);
+      this._events[event] = null;
+      return this;
+    }
+    // specific handler
+    var cb;
+    var i = cbs.length;
+    while (i--) {
+      cb = cbs[i];
+      if (cb === fn || cb.fn === fn) {
+        modifyListenerCount(this, event, -1);
+        cbs.splice(i, 1);
+        break;
+      }
+    }
+    return this;
+  };
+
+  /**
+   * Trigger an event on self.
+   *
+   * @param {String|Object} event
+   * @return {Boolean} shouldPropagate
+   */
+
+  Vue.prototype.$emit = function (event) {
+    var isSource = typeof event === 'string';
+    event = isSource ? event : event.name;
+    var cbs = this._events[event];
+    var shouldPropagate = isSource || !cbs;
+    if (cbs) {
+      cbs = cbs.length > 1 ? toArray(cbs) : cbs;
+      // this is a somewhat hacky solution to the question raised
+      // in #2102: for an inline component listener like <comp @test="doThis">,
+      // the propagation handling is somewhat broken. Therefore we
+      // need to treat these inline callbacks differently.
+      var hasParentCbs = isSource && cbs.some(function (cb) {
+        return cb._fromParent;
+      });
+      if (hasParentCbs) {
+        shouldPropagate = false;
+      }
+      var args = toArray(arguments, 1);
+      for (var i = 0, l = cbs.length; i < l; i++) {
+        var cb = cbs[i];
+        var res = cb.apply(this, args);
+        if (res === true && (!hasParentCbs || cb._fromParent)) {
+          shouldPropagate = true;
+        }
+      }
+    }
+    return shouldPropagate;
+  };
+
+  /**
+   * Recursively broadcast an event to all children instances.
+   *
+   * @param {String|Object} event
+   * @param {...*} additional arguments
+   */
+
+  Vue.prototype.$broadcast = function (event) {
+    var isSource = typeof event === 'string';
+    event = isSource ? event : event.name;
+    // if no child has registered for this event,
+    // then there's no need to broadcast.
+    if (!this._eventsCount[event]) return;
+    var children = this.$children;
+    var args = toArray(arguments);
+    if (isSource) {
+      // use object event to indicate non-source emit
+      // on children
+      args[0] = { name: event, source: this };
+    }
+    for (var i = 0, l = children.length; i < l; i++) {
+      var child = children[i];
+      var shouldPropagate = child.$emit.apply(child, args);
+      if (shouldPropagate) {
+        child.$broadcast.apply(child, args);
+      }
+    }
+    return this;
+  };
+
+  /**
+   * Recursively propagate an event up the parent chain.
+   *
+   * @param {String} event
+   * @param {...*} additional arguments
+   */
+
+  Vue.prototype.$dispatch = function (event) {
+    var shouldPropagate = this.$emit.apply(this, arguments);
+    if (!shouldPropagate) return;
+    var parent = this.$parent;
+    var args = toArray(arguments);
+    // use object event to indicate non-source emit
+    // on parents
+    args[0] = { name: event, source: this };
+    while (parent) {
+      shouldPropagate = parent.$emit.apply(parent, args);
+      parent = shouldPropagate ? parent.$parent : null;
+    }
+    return this;
+  };
+
+  /**
+   * Modify the listener counts on all parents.
+   * This bookkeeping allows $broadcast to return early when
+   * no child has listened to a certain event.
+   *
+   * @param {Vue} vm
+   * @param {String} event
+   * @param {Number} count
+   */
+
+  var hookRE = /^hook:/;
+  function modifyListenerCount(vm, event, count) {
+    var parent = vm.$parent;
+    // hooks do not get broadcasted so no need
+    // to do bookkeeping for them
+    if (!parent || !count || hookRE.test(event)) return;
+    while (parent) {
+      parent._eventsCount[event] = (parent._eventsCount[event] || 0) + count;
+      parent = parent.$parent;
+    }
+  }
+}
+
+function lifecycleAPI (Vue) {
+  /**
+   * Set instance target element and kick off the compilation
+   * process. The passed in `el` can be a selector string, an
+   * existing Element, or a DocumentFragment (for block
+   * instances).
+   *
+   * @param {Element|DocumentFragment|string} el
+   * @public
+   */
+
+  Vue.prototype.$mount = function (el) {
+    if (this._isCompiled) {
+      'development' !== 'production' && warn('$mount() should be called only once.', this);
+      return;
+    }
+    el = query(el);
+    if (!el) {
+      el = document.createElement('div');
+    }
+    this._compile(el);
+    this._initDOMHooks();
+    if (inDoc(this.$el)) {
+      this._callHook('attached');
+      ready.call(this);
+    } else {
+      this.$once('hook:attached', ready);
+    }
+    return this;
+  };
+
+  /**
+   * Mark an instance as ready.
+   */
+
+  function ready() {
+    this._isAttached = true;
+    this._isReady = true;
+    this._callHook('ready');
+  }
+
+  /**
+   * Teardown the instance, simply delegate to the internal
+   * _destroy.
+   *
+   * @param {Boolean} remove
+   * @param {Boolean} deferCleanup
+   */
+
+  Vue.prototype.$destroy = function (remove, deferCleanup) {
+    this._destroy(remove, deferCleanup);
+  };
+
+  /**
+   * Partially compile a piece of DOM and return a
+   * decompile function.
+   *
+   * @param {Element|DocumentFragment} el
+   * @param {Vue} [host]
+   * @param {Object} [scope]
+   * @param {Fragment} [frag]
+   * @return {Function}
+   */
+
+  Vue.prototype.$compile = function (el, host, scope, frag) {
+    return compile(el, this.$options, true)(this, el, host, scope, frag);
+  };
+}
+
+/**
+ * The exposed Vue constructor.
+ *
+ * API conventions:
+ * - public API methods/properties are prefixed with `$`
+ * - internal methods/properties are prefixed with `_`
+ * - non-prefixed properties are assumed to be proxied user
+ *   data.
+ *
+ * @constructor
+ * @param {Object} [options]
+ * @public
+ */
+
+function Vue(options) {
+  this._init(options);
+}
+
+// install internals
+initMixin(Vue);
+stateMixin(Vue);
+eventsMixin(Vue);
+lifecycleMixin(Vue);
+miscMixin(Vue);
+
+// install instance APIs
+dataAPI(Vue);
+domAPI(Vue);
+eventsAPI(Vue);
+lifecycleAPI(Vue);
+
+var slot = {
+
+  priority: SLOT,
+  params: ['name'],
+
+  bind: function bind() {
+    // this was resolved during component transclusion
+    var name = this.params.name || 'default';
+    var content = this.vm._slotContents && this.vm._slotContents[name];
+    if (!content || !content.hasChildNodes()) {
+      this.fallback();
+    } else {
+      this.compile(content.cloneNode(true), this.vm._context, this.vm);
+    }
+  },
+
+  compile: function compile(content, context, host) {
+    if (content && context) {
+      if (this.el.hasChildNodes() && content.childNodes.length === 1 && content.childNodes[0].nodeType === 1 && content.childNodes[0].hasAttribute('v-if')) {
+        // if the inserted slot has v-if
+        // inject fallback content as the v-else
+        var elseBlock = document.createElement('template');
+        elseBlock.setAttribute('v-else', '');
+        elseBlock.innerHTML = this.el.innerHTML;
+        // the else block should be compiled in child scope
+        elseBlock._context = this.vm;
+        content.appendChild(elseBlock);
+      }
+      var scope = host ? host._scope : this._scope;
+      this.unlink = context.$compile(content, host, scope, this._frag);
+    }
+    if (content) {
+      replace(this.el, content);
+    } else {
+      remove(this.el);
+    }
+  },
+
+  fallback: function fallback() {
+    this.compile(extractContent(this.el, true), this.vm);
+  },
+
+  unbind: function unbind() {
+    if (this.unlink) {
+      this.unlink();
+    }
+  }
+};
+
+var partial = {
+
+  priority: PARTIAL,
+
+  params: ['name'],
+
+  // watch changes to name for dynamic partials
+  paramWatchers: {
+    name: function name(value) {
+      vIf.remove.call(this);
+      if (value) {
+        this.insert(value);
+      }
+    }
+  },
+
+  bind: function bind() {
+    this.anchor = createAnchor('v-partial');
+    replace(this.el, this.anchor);
+    this.insert(this.params.name);
+  },
+
+  insert: function insert(id) {
+    var partial = resolveAsset(this.vm.$options, 'partials', id, true);
+    if (partial) {
+      this.factory = new FragmentFactory(this.vm, partial);
+      vIf.insert.call(this);
+    }
+  },
+
+  unbind: function unbind() {
+    if (this.frag) {
+      this.frag.destroy();
+    }
+  }
+};
+
+var elementDirectives = {
+  slot: slot,
+  partial: partial
+};
+
+var convertArray = vFor._postProcess;
+
+/**
+ * Limit filter for arrays
+ *
+ * @param {Number} n
+ * @param {Number} offset (Decimal expected)
+ */
+
+function limitBy(arr, n, offset) {
+  offset = offset ? parseInt(offset, 10) : 0;
+  n = toNumber(n);
+  return typeof n === 'number' ? arr.slice(offset, offset + n) : arr;
+}
+
+/**
+ * Filter filter for arrays
+ *
+ * @param {String} search
+ * @param {String} [delimiter]
+ * @param {String} ...dataKeys
+ */
+
+function filterBy(arr, search, delimiter) {
+  arr = convertArray(arr);
+  if (search == null) {
+    return arr;
+  }
+  if (typeof search === 'function') {
+    return arr.filter(search);
+  }
+  // cast to lowercase string
+  search = ('' + search).toLowerCase();
+  // allow optional `in` delimiter
+  // because why not
+  var n = delimiter === 'in' ? 3 : 2;
+  // extract and flatten keys
+  var keys = Array.prototype.concat.apply([], toArray(arguments, n));
+  var res = [];
+  var item, key, val, j;
+  for (var i = 0, l = arr.length; i < l; i++) {
+    item = arr[i];
+    val = item && item.$value || item;
+    j = keys.length;
+    if (j) {
+      while (j--) {
+        key = keys[j];
+        if (key === '$key' && contains(item.$key, search) || contains(getPath(val, key), search)) {
+          res.push(item);
+          break;
+        }
+      }
+    } else if (contains(item, search)) {
+      res.push(item);
+    }
+  }
+  return res;
+}
+
+/**
+ * Order filter for arrays
+ *
+ * @param {String|Array<String>|Function} ...sortKeys
+ * @param {Number} [order]
+ */
+
+function orderBy(arr) {
+  var comparator = null;
+  var sortKeys = undefined;
+  arr = convertArray(arr);
+
+  // determine order (last argument)
+  var args = toArray(arguments, 1);
+  var order = args[args.length - 1];
+  if (typeof order === 'number') {
+    order = order < 0 ? -1 : 1;
+    args = args.length > 1 ? args.slice(0, -1) : args;
+  } else {
+    order = 1;
+  }
+
+  // determine sortKeys & comparator
+  var firstArg = args[0];
+  if (!firstArg) {
+    return arr;
+  } else if (typeof firstArg === 'function') {
+    // custom comparator
+    comparator = function (a, b) {
+      return firstArg(a, b) * order;
+    };
+  } else {
+    // string keys. flatten first
+    sortKeys = Array.prototype.concat.apply([], args);
+    comparator = function (a, b, i) {
+      i = i || 0;
+      return i >= sortKeys.length - 1 ? baseCompare(a, b, i) : baseCompare(a, b, i) || comparator(a, b, i + 1);
+    };
+  }
+
+  function baseCompare(a, b, sortKeyIndex) {
+    var sortKey = sortKeys[sortKeyIndex];
+    if (sortKey) {
+      if (sortKey !== '$key') {
+        if (isObject(a) && '$value' in a) a = a.$value;
+        if (isObject(b) && '$value' in b) b = b.$value;
+      }
+      a = isObject(a) ? getPath(a, sortKey) : a;
+      b = isObject(b) ? getPath(b, sortKey) : b;
+    }
+    return a === b ? 0 : a > b ? order : -order;
+  }
+
+  // sort on a copy to avoid mutating original array
+  return arr.slice().sort(comparator);
+}
+
+/**
+ * String contain helper
+ *
+ * @param {*} val
+ * @param {String} search
+ */
+
+function contains(val, search) {
+  var i;
+  if (isPlainObject(val)) {
+    var keys = Object.keys(val);
+    i = keys.length;
+    while (i--) {
+      if (contains(val[keys[i]], search)) {
+        return true;
+      }
+    }
+  } else if (isArray(val)) {
+    i = val.length;
+    while (i--) {
+      if (contains(val[i], search)) {
+        return true;
+      }
+    }
+  } else if (val != null) {
+    return val.toString().toLowerCase().indexOf(search) > -1;
+  }
+}
+
+var digitsRE = /(\d{3})(?=\d)/g;
+
+// asset collections must be a plain object.
+var filters = {
+
+  orderBy: orderBy,
+  filterBy: filterBy,
+  limitBy: limitBy,
+
+  /**
+   * Stringify value.
+   *
+   * @param {Number} indent
+   */
+
+  json: {
+    read: function read(value, indent) {
+      return typeof value === 'string' ? value : JSON.stringify(value, null, arguments.length > 1 ? indent : 2);
+    },
+    write: function write(value) {
+      try {
+        return JSON.parse(value);
+      } catch (e) {
+        return value;
+      }
+    }
+  },
+
+  /**
+   * 'abc' => 'Abc'
+   */
+
+  capitalize: function capitalize(value) {
+    if (!value && value !== 0) return '';
+    value = value.toString();
+    return value.charAt(0).toUpperCase() + value.slice(1);
+  },
+
+  /**
+   * 'abc' => 'ABC'
+   */
+
+  uppercase: function uppercase(value) {
+    return value || value === 0 ? value.toString().toUpperCase() : '';
+  },
+
+  /**
+   * 'AbC' => 'abc'
+   */
+
+  lowercase: function lowercase(value) {
+    return value || value === 0 ? value.toString().toLowerCase() : '';
+  },
+
+  /**
+   * 12345 => $12,345.00
+   *
+   * @param {String} sign
+   * @param {Number} decimals Decimal places
+   */
+
+  currency: function currency(value, _currency, decimals) {
+    value = parseFloat(value);
+    if (!isFinite(value) || !value && value !== 0) return '';
+    _currency = _currency != null ? _currency : '$';
+    decimals = decimals != null ? decimals : 2;
+    var stringified = Math.abs(value).toFixed(decimals);
+    var _int = decimals ? stringified.slice(0, -1 - decimals) : stringified;
+    var i = _int.length % 3;
+    var head = i > 0 ? _int.slice(0, i) + (_int.length > 3 ? ',' : '') : '';
+    var _float = decimals ? stringified.slice(-1 - decimals) : '';
+    var sign = value < 0 ? '-' : '';
+    return sign + _currency + head + _int.slice(i).replace(digitsRE, '$1,') + _float;
+  },
+
+  /**
+   * 'item' => 'items'
+   *
+   * @params
+   *  an array of strings corresponding to
+   *  the single, double, triple ... forms of the word to
+   *  be pluralized. When the number to be pluralized
+   *  exceeds the length of the args, it will use the last
+   *  entry in the array.
+   *
+   *  e.g. ['single', 'double', 'triple', 'multiple']
+   */
+
+  pluralize: function pluralize(value) {
+    var args = toArray(arguments, 1);
+    var length = args.length;
+    if (length > 1) {
+      var index = value % 10 - 1;
+      return index in args ? args[index] : args[length - 1];
+    } else {
+      return args[0] + (value === 1 ? '' : 's');
+    }
+  },
+
+  /**
+   * Debounce a handler function.
+   *
+   * @param {Function} handler
+   * @param {Number} delay = 300
+   * @return {Function}
+   */
+
+  debounce: function debounce(handler, delay) {
+    if (!handler) return;
+    if (!delay) {
+      delay = 300;
+    }
+    return _debounce(handler, delay);
+  }
+};
+
+function installGlobalAPI (Vue) {
+  /**
+   * Vue and every constructor that extends Vue has an
+   * associated options object, which can be accessed during
+   * compilation steps as `this.constructor.options`.
+   *
+   * These can be seen as the default options of every
+   * Vue instance.
+   */
+
+  Vue.options = {
+    directives: directives,
+    elementDirectives: elementDirectives,
+    filters: filters,
+    transitions: {},
+    components: {},
+    partials: {},
+    replace: true
+  };
+
+  /**
+   * Expose useful internals
+   */
+
+  Vue.util = util;
+  Vue.config = config;
+  Vue.set = set;
+  Vue['delete'] = del;
+  Vue.nextTick = nextTick;
+
+  /**
+   * The following are exposed for advanced usage / plugins
+   */
+
+  Vue.compiler = compiler;
+  Vue.FragmentFactory = FragmentFactory;
+  Vue.internalDirectives = internalDirectives;
+  Vue.parsers = {
+    path: path,
+    text: text,
+    template: template,
+    directive: directive,
+    expression: expression
+  };
+
+  /**
+   * Each instance constructor, including Vue, has a unique
+   * cid. This enables us to create wrapped "child
+   * constructors" for prototypal inheritance and cache them.
+   */
+
+  Vue.cid = 0;
+  var cid = 1;
+
+  /**
+   * Class inheritance
+   *
+   * @param {Object} extendOptions
+   */
+
+  Vue.extend = function (extendOptions) {
+    extendOptions = extendOptions || {};
+    var Super = this;
+    var isFirstExtend = Super.cid === 0;
+    if (isFirstExtend && extendOptions._Ctor) {
+      return extendOptions._Ctor;
+    }
+    var name = extendOptions.name || Super.options.name;
+    if ('development' !== 'production') {
+      if (!/^[a-zA-Z][\w-]*$/.test(name)) {
+        warn('Invalid component name: "' + name + '". Component names ' + 'can only contain alphanumeric characaters and the hyphen.');
+        name = null;
+      }
+    }
+    var Sub = createClass(name || 'VueComponent');
+    Sub.prototype = Object.create(Super.prototype);
+    Sub.prototype.constructor = Sub;
+    Sub.cid = cid++;
+    Sub.options = mergeOptions(Super.options, extendOptions);
+    Sub['super'] = Super;
+    // allow further extension
+    Sub.extend = Super.extend;
+    // create asset registers, so extended classes
+    // can have their private assets too.
+    config._assetTypes.forEach(function (type) {
+      Sub[type] = Super[type];
+    });
+    // enable recursive self-lookup
+    if (name) {
+      Sub.options.components[name] = Sub;
+    }
+    // cache constructor
+    if (isFirstExtend) {
+      extendOptions._Ctor = Sub;
+    }
+    return Sub;
+  };
+
+  /**
+   * A function that returns a sub-class constructor with the
+   * given name. This gives us much nicer output when
+   * logging instances in the console.
+   *
+   * @param {String} name
+   * @return {Function}
+   */
+
+  function createClass(name) {
+    /* eslint-disable no-new-func */
+    return new Function('return function ' + classify(name) + ' (options) { this._init(options) }')();
+    /* eslint-enable no-new-func */
+  }
+
+  /**
+   * Plugin system
+   *
+   * @param {Object} plugin
+   */
+
+  Vue.use = function (plugin) {
+    /* istanbul ignore if */
+    if (plugin.installed) {
+      return;
+    }
+    // additional parameters
+    var args = toArray(arguments, 1);
+    args.unshift(this);
+    if (typeof plugin.install === 'function') {
+      plugin.install.apply(plugin, args);
+    } else {
+      plugin.apply(null, args);
+    }
+    plugin.installed = true;
+    return this;
+  };
+
+  /**
+   * Apply a global mixin by merging it into the default
+   * options.
+   */
+
+  Vue.mixin = function (mixin) {
+    Vue.options = mergeOptions(Vue.options, mixin);
+  };
+
+  /**
+   * Create asset registration methods with the following
+   * signature:
+   *
+   * @param {String} id
+   * @param {*} definition
+   */
+
+  config._assetTypes.forEach(function (type) {
+    Vue[type] = function (id, definition) {
+      if (!definition) {
+        return this.options[type + 's'][id];
+      } else {
+        /* istanbul ignore if */
+        if ('development' !== 'production') {
+          if (type === 'component' && (commonTagRE.test(id) || reservedTagRE.test(id))) {
+            warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + id);
+          }
+        }
+        if (type === 'component' && isPlainObject(definition)) {
+          if (!definition.name) {
+            definition.name = id;
+          }
+          definition = Vue.extend(definition);
+        }
+        this.options[type + 's'][id] = definition;
+        return definition;
+      }
+    };
+  });
+
+  // expose internal transition API
+  extend(Vue.transition, transition);
+}
+
+installGlobalAPI(Vue);
+
+Vue.version = '1.0.28';
+
+// devtools global hook
+/* istanbul ignore next */
+setTimeout(function () {
+  if (config.devtools) {
+    if (devtools) {
+      devtools.emit('init', Vue);
+    } else if ('development' !== 'production' && inBrowser && /Chrome\/\d+/.test(window.navigator.userAgent)) {
+      console.log('Download the Vue Devtools for a better development experience:\n' + 'https://github.com/vuejs/vue-devtools');
+    }
+  }
+}, 0);
+
+return Vue;
+
+})));
\ No newline at end of file
diff --git a/dist/vue.min.js b/dist/vue.min.js
new file mode 100644
index 00000000000..4c19184577e
--- /dev/null
+++ b/dist/vue.min.js
@@ -0,0 +1,9 @@
+/*!
+ * Vue.js v1.0.28
+ * (c) 2016 Evan You
+ * Released under the MIT License.
+ */
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.Vue=e()}(this,function(){"use strict";function t(e,n,r){if(i(e,n))return void(e[n]=r);if(e._isVue)return void t(e._data,n,r);var s=e.__ob__;if(!s)return void(e[n]=r);if(s.convert(n,r),s.dep.notify(),s.vms)for(var o=s.vms.length;o--;){var a=s.vms[o];a._proxy(n),a._digest()}return r}function e(t,e){if(i(t,e)){delete t[e];var n=t.__ob__;if(!n)return void(t._isVue&&(delete t._data[e],t._digest()));if(n.dep.notify(),n.vms)for(var r=n.vms.length;r--;){var s=n.vms[r];s._unproxy(e),s._digest()}}}function i(t,e){return Mi.call(t,e)}function n(t){return Wi.test(t)}function r(t){var e=(t+"").charCodeAt(0);return 36===e||95===e}function s(t){return null==t?"":t.toString()}function o(t){if("string"!=typeof t)return t;var e=Number(t);return isNaN(e)?t:e}function a(t){return"true"===t||"false"!==t&&t}function h(t){var e=t.charCodeAt(0),i=t.charCodeAt(t.length-1);return e!==i||34!==e&&39!==e?t:t.slice(1,-1)}function l(t){return t.replace(Vi,c)}function c(t,e){return e?e.toUpperCase():""}function u(t){return t.replace(Bi,"$1-$2").replace(Bi,"$1-$2").toLowerCase()}function f(t){return t.replace(zi,c)}function p(t,e){return function(i){var n=arguments.length;return n?n>1?t.apply(e,arguments):t.call(e,i):t.call(e)}}function d(t,e){e=e||0;for(var i=t.length-e,n=new Array(i);i--;)n[i]=t[i+e];return n}function v(t,e){for(var i=Object.keys(e),n=i.length;n--;)t[i[n]]=e[i[n]];return t}function m(t){return null!==t&&"object"==typeof t}function g(t){return Ui.call(t)===Ji}function _(t,e,i,n){Object.defineProperty(t,e,{value:i,enumerable:!!n,writable:!0,configurable:!0})}function y(t,e){var i,n,r,s,o,a=function a(){var h=Date.now()-s;h<e&&h>=0?i=setTimeout(a,e-h):(i=null,o=t.apply(r,n),i||(r=n=null))};return function(){return r=this,n=arguments,s=Date.now(),i||(i=setTimeout(a,e)),o}}function b(t,e){for(var i=t.length;i--;)if(t[i]===e)return i;return-1}function w(t){var e=function e(){if(!e.cancelled)return t.apply(this,arguments)};return e.cancel=function(){e.cancelled=!0},e}function C(t,e){return t==e||!(!m(t)||!m(e))&&JSON.stringify(t)===JSON.stringify(e)}function $(t){return/native code/.test(t.toString())}function k(t){this.size=0,this.limit=t,this.head=this.tail=void 0,this._keymap=Object.create(null)}function x(){return fn.charCodeAt(vn+1)}function A(){return fn.charCodeAt(++vn)}function O(){return vn>=dn}function T(){for(;x()===Tn;)A()}function N(t){return t===kn||t===xn}function j(t){return Nn[t]}function E(t,e){return jn[t]===e}function S(){for(var t,e=A();!O();)if(t=A(),t===On)A();else if(t===e)break}function F(t){for(var e=0,i=t;!O();)if(t=x(),N(t))S();else if(i===t&&e++,E(i,t)&&e--,A(),0===e)break}function D(){for(var t=vn;!O();)if(mn=x(),N(mn))S();else if(j(mn))F(mn);else if(mn===An){if(A(),mn=x(),mn!==An){gn!==bn&&gn!==$n||(gn=wn);break}A()}else{if(mn===Tn&&(gn===Cn||gn===$n)){T();break}gn===wn&&(gn=Cn),A()}return fn.slice(t+1,vn)||null}function P(){for(var t=[];!O();)t.push(R());return t}function R(){var t,e={};return gn=wn,e.name=D().trim(),gn=$n,t=L(),t.length&&(e.args=t),e}function L(){for(var t=[];!O()&&gn!==wn;){var e=D();if(!e)break;t.push(H(e))}return t}function H(t){if(yn.test(t))return{value:o(t),dynamic:!1};var e=h(t),i=e===t;return{value:i?t:e,dynamic:i}}function I(t){var e=_n.get(t);if(e)return e;fn=t,pn={},dn=fn.length,vn=-1,mn="",gn=bn;var i;return fn.indexOf("|")<0?pn.expression=fn.trim():(pn.expression=D().trim(),i=P(),i.length&&(pn.filters=i)),_n.put(t,pn),pn}function M(t){return t.replace(Sn,"\\$&")}function W(){var t=M(Mn.delimiters[0]),e=M(Mn.delimiters[1]),i=M(Mn.unsafeDelimiters[0]),n=M(Mn.unsafeDelimiters[1]);Dn=new RegExp(i+"((?:.|\\n)+?)"+n+"|"+t+"((?:.|\\n)+?)"+e,"g"),Pn=new RegExp("^"+i+"((?:.|\\n)+?)"+n+"$"),Fn=new k(1e3)}function V(t){Fn||W();var e=Fn.get(t);if(e)return e;if(!Dn.test(t))return null;for(var i,n,r,s,o,a,h=[],l=Dn.lastIndex=0;i=Dn.exec(t);)n=i.index,n>l&&h.push({value:t.slice(l,n)}),r=Pn.test(i[0]),s=r?i[1]:i[2],o=s.charCodeAt(0),a=42===o,s=a?s.slice(1):s,h.push({tag:!0,value:s.trim(),html:r,oneTime:a}),l=n+i[0].length;return l<t.length&&h.push({value:t.slice(l)}),Fn.put(t,h),h}function B(t,e){return t.length>1?t.map(function(t){return z(t,e)}).join("+"):z(t[0],e,!0)}function z(t,e,i){return t.tag?t.oneTime&&e?'"'+e.$eval(t.value)+'"':U(t.value,i):'"'+t.value+'"'}function U(t,e){if(Rn.test(t)){var i=I(t);return i.filters?"this._applyFilters("+i.expression+",null,"+JSON.stringify(i.filters)+",false)":"("+t+")"}return e?t:"("+t+")"}function J(t,e,i,n){G(t,1,function(){e.appendChild(t)},i,n)}function q(t,e,i,n){G(t,1,function(){et(t,e)},i,n)}function Q(t,e,i){G(t,-1,function(){nt(t)},e,i)}function G(t,e,i,n,r){var s=t.__v_trans;if(!s||!s.hooks&&!rn||!n._isCompiled||n.$parent&&!n.$parent._isCompiled)return i(),void(r&&r());var o=e>0?"enter":"leave";s[o](i,r)}function Z(t){if("string"==typeof t){t=document.querySelector(t)}return t}function X(t){if(!t)return!1;var e=t.ownerDocument.documentElement,i=t.parentNode;return e===t||e===i||!(!i||1!==i.nodeType||!e.contains(i))}function Y(t,e){var i=t.getAttribute(e);return null!==i&&t.removeAttribute(e),i}function K(t,e){var i=Y(t,":"+e);return null===i&&(i=Y(t,"v-bind:"+e)),i}function tt(t,e){return t.hasAttribute(e)||t.hasAttribute(":"+e)||t.hasAttribute("v-bind:"+e)}function et(t,e){e.parentNode.insertBefore(t,e)}function it(t,e){e.nextSibling?et(t,e.nextSibling):e.parentNode.appendChild(t)}function nt(t){t.parentNode.removeChild(t)}function rt(t,e){e.firstChild?et(t,e.firstChild):e.appendChild(t)}function st(t,e){var i=t.parentNode;i&&i.replaceChild(e,t)}function ot(t,e,i,n){t.addEventListener(e,i,n)}function at(t,e,i){t.removeEventListener(e,i)}function ht(t){var e=t.className;return"object"==typeof e&&(e=e.baseVal||""),e}function lt(t,e){Ki&&!/svg$/.test(t.namespaceURI)?t.className=e:t.setAttribute("class",e)}function ct(t,e){if(t.classList)t.classList.add(e);else{var i=" "+ht(t)+" ";i.indexOf(" "+e+" ")<0&&lt(t,(i+e).trim())}}function ut(t,e){if(t.classList)t.classList.remove(e);else{for(var i=" "+ht(t)+" ",n=" "+e+" ";i.indexOf(n)>=0;)i=i.replace(n," ");lt(t,i.trim())}t.className||t.removeAttribute("class")}function ft(t,e){var i,n;if(vt(t)&&bt(t.content)&&(t=t.content),t.hasChildNodes())for(pt(t),n=e?document.createDocumentFragment():document.createElement("div");i=t.firstChild;)n.appendChild(i);return n}function pt(t){for(var e;e=t.firstChild,dt(e);)t.removeChild(e);for(;e=t.lastChild,dt(e);)t.removeChild(e)}function dt(t){return t&&(3===t.nodeType&&!t.data.trim()||8===t.nodeType)}function vt(t){return t.tagName&&"template"===t.tagName.toLowerCase()}function mt(t,e){var i=Mn.debug?document.createComment(t):document.createTextNode(e?" ":"");return i.__v_anchor=!0,i}function gt(t){if(t.hasAttributes())for(var e=t.attributes,i=0,n=e.length;i<n;i++){var r=e[i].name;if(Bn.test(r))return l(r.replace(Bn,""))}}function _t(t,e,i){for(var n;t!==e;)n=t.nextSibling,i(t),t=n;i(e)}function yt(t,e,i,n,r){function s(){if(a++,o&&a>=h.length){for(var t=0;t<h.length;t++)n.appendChild(h[t]);r&&r()}}var o=!1,a=0,h=[];_t(t,e,function(t){t===e&&(o=!0),h.push(t),Q(t,i,s)})}function bt(t){return t&&11===t.nodeType}function wt(t){if(t.outerHTML)return t.outerHTML;var e=document.createElement("div");return e.appendChild(t.cloneNode(!0)),e.innerHTML}function Ct(t,e){var i=t.tagName.toLowerCase(),n=t.hasAttributes();if(zn.test(i)||Un.test(i)){if(n)return $t(t,e)}else{if(jt(e,"components",i))return{id:i};var r=n&&$t(t,e);if(r)return r}}function $t(t,e){var i=t.getAttribute("is");if(null!=i){if(jt(e,"components",i))return t.removeAttribute("is"),{id:i}}else if(i=K(t,"is"),null!=i)return{id:i,dynamic:!0}}function kt(e,n){var r,s,o;for(r in n)s=e[r],o=n[r],i(e,r)?m(s)&&m(o)&&kt(s,o):t(e,r,o);return e}function xt(t,e){var i=Object.create(t||null);return e?v(i,Tt(e)):i}function At(t){if(t.components)for(var e,i=t.components=Tt(t.components),n=Object.keys(i),r=0,s=n.length;r<s;r++){var o=n[r];zn.test(o)||Un.test(o)||(e=i[o],g(e)&&(i[o]=Di.extend(e)))}}function Ot(t){var e,i,n=t.props;if(qi(n))for(t.props={},e=n.length;e--;)i=n[e],"string"==typeof i?t.props[i]=null:i.name&&(t.props[i.name]=i);else if(g(n)){var r=Object.keys(n);for(e=r.length;e--;)i=n[r[e]],"function"==typeof i&&(n[r[e]]={type:i})}}function Tt(t){if(qi(t)){for(var e,i={},n=t.length;n--;){e=t[n];var r="function"==typeof e?e.options&&e.options.name||e.id:e.name||e.id;r&&(i[r]=e)}return i}return t}function Nt(t,e,n){function r(i){var r=Jn[i]||qn;o[i]=r(t[i],e[i],n,i)}At(e),Ot(e);var s,o={};if(e.extends&&(t="function"==typeof e.extends?Nt(t,e.extends.options,n):Nt(t,e.extends,n)),e.mixins)for(var a=0,h=e.mixins.length;a<h;a++){var l=e.mixins[a],c=l.prototype instanceof Di?l.options:l;t=Nt(t,c,n)}for(s in t)r(s);for(s in e)i(t,s)||r(s);return o}function jt(t,e,i,n){if("string"==typeof i){var r,s=t[e],o=s[i]||s[r=l(i)]||s[r.charAt(0).toUpperCase()+r.slice(1)];return o}}function Et(){this.id=Qn++,this.subs=[]}function St(t){Yn=!1,t(),Yn=!0}function Ft(t){if(this.value=t,this.dep=new Et,_(t,"__ob__",this),qi(t)){var e=Qi?Dt:Pt;e(t,Zn,Xn),this.observeArray(t)}else this.walk(t)}function Dt(t,e){t.__proto__=e}function Pt(t,e,i){for(var n=0,r=i.length;n<r;n++){var s=i[n];_(t,s,e[s])}}function Rt(t,e){if(t&&"object"==typeof t){var n;return i(t,"__ob__")&&t.__ob__ instanceof Ft?n=t.__ob__:Yn&&(qi(t)||g(t))&&Object.isExtensible(t)&&!t._isVue&&(n=new Ft(t)),n&&e&&n.addVm(e),n}}function Lt(t,e,i){var n=new Et,r=Object.getOwnPropertyDescriptor(t,e);if(!r||r.configurable!==!1){var s=r&&r.get,o=r&&r.set,a=Rt(i);Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){var e=s?s.call(t):i;if(Et.target&&(n.depend(),a&&a.dep.depend(),qi(e)))for(var r,o=0,h=e.length;o<h;o++)r=e[o],r&&r.__ob__&&r.__ob__.dep.depend();return e},set:function(e){var r=s?s.call(t):i;e!==r&&(o?o.call(t,e):i=e,a=Rt(e),n.notify())}})}}function Ht(t){t.prototype._init=function(t){t=t||{},this.$el=null,this.$parent=t.parent,this.$root=this.$parent?this.$parent.$root:this,this.$children=[],this.$refs={},this.$els={},this._watchers=[],this._directives=[],this._uid=tr++,this._isVue=!0,this._events={},this._eventsCount={},this._isFragment=!1,this._fragment=this._fragmentStart=this._fragmentEnd=null,this._isCompiled=this._isDestroyed=this._isReady=this._isAttached=this._isBeingDestroyed=this._vForRemoving=!1,this._unlinkFn=null,this._context=t._context||this.$parent,this._scope=t._scope,this._frag=t._frag,this._frag&&this._frag.children.push(this),this.$parent&&this.$parent.$children.push(this),t=this.$options=Nt(this.constructor.options,t,this),this._updateRef(),this._data={},this._callHook("init"),this._initState(),this._initEvents(),this._callHook("created"),t.el&&this.$mount(t.el)}}function It(t){if(void 0===t)return"eof";var e=t.charCodeAt(0);switch(e){case 91:case 93:case 46:case 34:case 39:case 48:return t;case 95:case 36:return"ident";case 32:case 9:case 10:case 13:case 160:case 65279:case 8232:case 8233:return"ws"}return e>=97&&e<=122||e>=65&&e<=90?"ident":e>=49&&e<=57?"number":"else"}function Mt(t){var e=t.trim();return("0"!==t.charAt(0)||!isNaN(t))&&(n(e)?h(e):"*"+e)}function Wt(t){function e(){var e=t[c+1];if(u===ur&&"'"===e||u===fr&&'"'===e)return c++,n="\\"+e,p[ir](),!0}var i,n,r,s,o,a,h,l=[],c=-1,u=or,f=0,p=[];for(p[nr]=function(){void 0!==r&&(l.push(r),r=void 0)},p[ir]=function(){void 0===r?r=n:r+=n},p[rr]=function(){p[ir](),f++},p[sr]=function(){if(f>0)f--,u=cr,p[ir]();else{if(f=0,r=Mt(r),r===!1)return!1;p[nr]()}};null!=u;)if(c++,i=t[c],"\\"!==i||!e()){if(s=It(i),h=vr[u],o=h[s]||h.else||dr,o===dr)return;if(u=o[0],a=p[o[1]],a&&(n=o[2],n=void 0===n?i:n,a()===!1))return;if(u===pr)return l.raw=t,l}}function Vt(t){var e=er.get(t);return e||(e=Wt(t),e&&er.put(t,e)),e}function Bt(t,e){return Yt(e).get(t)}function zt(e,i,n){var r=e;if("string"==typeof i&&(i=Wt(i)),!i||!m(e))return!1;for(var s,o,a=0,h=i.length;a<h;a++)s=e,o=i[a],"*"===o.charAt(0)&&(o=Yt(o.slice(1)).get.call(r,r)),a<h-1?(e=e[o],m(e)||(e={},t(s,o,e))):qi(e)?e.$set(o,n):o in e?e[o]=n:t(e,o,n);return!0}function Ut(){}function Jt(t,e){var i=Nr.length;return Nr[i]=e?t.replace($r,"\\n"):t,'"'+i+'"'}function qt(t){var e=t.charAt(0),i=t.slice(1);return yr.test(i)?t:(i=i.indexOf('"')>-1?i.replace(xr,Qt):i,e+"scope."+i)}function Qt(t,e){return Nr[e]}function Gt(t){wr.test(t),Nr.length=0;var e=t.replace(kr,Jt).replace(Cr,"");return e=(" "+e).replace(Or,qt).replace(xr,Qt),Zt(e)}function Zt(t){try{return new Function("scope","return "+t+";")}catch(t){return Ut}}function Xt(t){var e=Vt(t);if(e)return function(t,i){zt(t,e,i)}}function Yt(t,e){t=t.trim();var i=gr.get(t);if(i)return e&&!i.set&&(i.set=Xt(i.exp)),i;var n={exp:t};return n.get=Kt(t)&&t.indexOf("[")<0?Zt("scope."+t):Gt(t),e&&(n.set=Xt(t)),gr.put(t,n),n}function Kt(t){return Ar.test(t)&&!Tr.test(t)&&"Math."!==t.slice(0,5)}function te(){Er.length=0,Sr.length=0,Fr={},Dr={},Pr=!1}function ee(){for(var t=!0;t;)t=!1,ie(Er),ie(Sr),Er.length?t=!0:(Zi&&Mn.devtools&&Zi.emit("flush"),te())}function ie(t){for(var e=0;e<t.length;e++){var i=t[e],n=i.id;Fr[n]=null,i.run()}t.length=0}function ne(t){var e=t.id;if(null==Fr[e]){var i=t.user?Sr:Er;Fr[e]=i.length,i.push(t),Pr||(Pr=!0,ln(ee))}}function re(t,e,i,n){n&&v(this,n);var r="function"==typeof e;if(this.vm=t,t._watchers.push(this),this.expression=e,this.cb=i,this.id=++Rr,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new cn,this.newDepIds=new cn,this.prevError=null,r)this.getter=e,this.setter=void 0;else{var s=Yt(e,this.twoWay);this.getter=s.get,this.setter=s.set}this.value=this.lazy?void 0:this.get(),this.queued=this.shallow=!1}function se(t,e){var i=void 0,n=void 0;e||(e=Lr,e.clear());var r=qi(t),s=m(t);if((r||s)&&Object.isExtensible(t)){if(t.__ob__){var o=t.__ob__.dep.id;if(e.has(o))return;e.add(o)}if(r)for(i=t.length;i--;)se(t[i],e);else if(s)for(n=Object.keys(t),i=n.length;i--;)se(t[n[i]],e)}}function oe(t){return vt(t)&&bt(t.content)}function ae(t,e){var i=e?t:t.trim(),n=Ir.get(i);if(n)return n;var r=document.createDocumentFragment(),s=t.match(Vr),o=Br.test(t),a=zr.test(t);if(s||o||a){var h=s&&s[1],l=Wr[h]||Wr.efault,c=l[0],u=l[1],f=l[2],p=document.createElement("div");for(p.innerHTML=u+t+f;c--;)p=p.lastChild;for(var d;d=p.firstChild;)r.appendChild(d)}else r.appendChild(document.createTextNode(t));return e||pt(r),Ir.put(i,r),r}function he(t){if(oe(t))return ae(t.innerHTML);if("SCRIPT"===t.tagName)return ae(t.textContent);for(var e,i=le(t),n=document.createDocumentFragment();e=i.firstChild;)n.appendChild(e);return pt(n),n}function le(t){if(!t.querySelectorAll)return t.cloneNode();var e,i,n,r=t.cloneNode(!0);if(Ur){var s=r;if(oe(t)&&(t=t.content,s=r.content),i=t.querySelectorAll("template"),i.length)for(n=s.querySelectorAll("template"),e=n.length;e--;)n[e].parentNode.replaceChild(le(i[e]),n[e])}if(Jr)if("TEXTAREA"===t.tagName)r.value=t.value;else if(i=t.querySelectorAll("textarea"),i.length)for(n=r.querySelectorAll("textarea"),e=n.length;e--;)n[e].value=i[e].value;return r}function ce(t,e,i){var n,r;return bt(t)?(pt(t),e?le(t):t):("string"==typeof t?i||"#"!==t.charAt(0)?r=ae(t,i):(r=Mr.get(t),r||(n=document.getElementById(t.slice(1)),n&&(r=he(n),Mr.put(t,r)))):t.nodeType&&(r=he(t)),r&&e?le(r):r)}function ue(t,e,i,n,r,s){this.children=[],this.childFrags=[],this.vm=e,this.scope=r,this.inserted=!1,this.parentFrag=s,s&&s.childFrags.push(this),this.unlink=t(e,i,n,r,this);var o=this.single=1===i.childNodes.length&&!i.childNodes[0].__v_anchor;o?(this.node=i.childNodes[0],this.before=fe,this.remove=pe):(this.node=mt("fragment-start"),this.end=mt("fragment-end"),this.frag=i,rt(this.node,i),i.appendChild(this.end),this.before=de,this.remove=ve),this.node.__v_frag=this}function fe(t,e){this.inserted=!0;var i=e!==!1?q:et;i(this.node,t,this.vm),X(this.node)&&this.callHook(me)}function pe(){this.inserted=!1;var t=X(this.node),e=this;this.beforeRemove(),Q(this.node,this.vm,function(){t&&e.callHook(ge),e.destroy()})}function de(t,e){this.inserted=!0;var i=this.vm,n=e!==!1?q:et;_t(this.node,this.end,function(e){n(e,t,i)}),X(this.node)&&this.callHook(me)}function ve(){this.inserted=!1;var t=this,e=X(this.node);this.beforeRemove(),yt(this.node,this.end,this.vm,this.frag,function(){e&&t.callHook(ge),t.destroy()})}function me(t){!t._isAttached&&X(t.$el)&&t._callHook("attached")}function ge(t){t._isAttached&&!X(t.$el)&&t._callHook("detached")}function _e(t,e){this.vm=t;var i,n="string"==typeof e;n||vt(e)&&!e.hasAttribute("v-if")?i=ce(e,!0):(i=document.createDocumentFragment(),i.appendChild(e)),this.template=i;var r,s=t.constructor.cid;if(s>0){var o=s+(n?e:wt(e));r=Gr.get(o),r||(r=qe(i,t.$options,!0),Gr.put(o,r))}else r=qe(i,t.$options,!0);this.linker=r}function ye(t,e,i){var n=t.node.previousSibling;if(n){for(t=n.__v_frag;!(t&&t.forId===i&&t.inserted||n===e);){if(n=n.previousSibling,!n)return;t=n.__v_frag}return t}}function be(t){for(var e=-1,i=new Array(Math.floor(t));++e<t;)i[e]=e;return i}function we(t,e,i,n){return n?"$index"===n?t:n.charAt(0).match(/\w/)?Bt(i,n):i[n]:e||i}function Ce(t){var e=t.node;if(t.end)for(;!e.__vue__&&e!==t.end&&e.nextSibling;)e=e.nextSibling;return e.__vue__}function $e(t,e,i){for(var n,r,s,o=e?[]:null,a=0,h=t.options.length;a<h;a++)if(n=t.options[a],s=i?n.hasAttribute("selected"):n.selected){if(r=n.hasOwnProperty("_value")?n._value:n.value,!e)return r;o.push(r)}return o}function ke(t,e){for(var i=t.length;i--;)if(C(t[i],e))return i;return-1}function xe(t,e){var i=e.map(function(t){var e=t.charCodeAt(0);return e>47&&e<58?parseInt(t,10):1===t.length&&(e=t.toUpperCase().charCodeAt(0),e>64&&e<91)?e:ms[t]});return i=[].concat.apply([],i),function(e){if(i.indexOf(e.keyCode)>-1)return t.call(this,e)}}function Ae(t){return function(e){return e.stopPropagation(),t.call(this,e)}}function Oe(t){return function(e){return e.preventDefault(),t.call(this,e)}}function Te(t){return function(e){if(e.target===e.currentTarget)return t.call(this,e)}}function Ne(t){if(ws[t])return ws[t];var e=je(t);return ws[t]=ws[e]=e,e}function je(t){t=u(t);var e=l(t),i=e.charAt(0).toUpperCase()+e.slice(1);Cs||(Cs=document.createElement("div"));var n,r=_s.length;if("filter"!==e&&e in Cs.style)return{kebab:t,camel:e};for(;r--;)if(n=ys[r]+i,n in Cs.style)return{kebab:_s[r]+t,camel:n}}function Ee(t){var e=[];if(qi(t))for(var i=0,n=t.length;i<n;i++){var r=t[i];if(r)if("string"==typeof r)e.push(r);else for(var s in r)r[s]&&e.push(s)}else if(m(t))for(var o in t)t[o]&&e.push(o);return e}function Se(t,e,i){if(e=e.trim(),e.indexOf(" ")===-1)return void i(t,e);for(var n=e.split(/\s+/),r=0,s=n.length;r<s;r++)i(t,n[r])}function Fe(t,e,i){function n(){++s>=r?i():t[s].call(e,n)}var r=t.length,s=0;t[0].call(e,n)}function De(t,e,i){for(var r,s,o,a,h,c,f,p=[],d=i.$options.propsData,v=Object.keys(e),m=v.length;m--;)s=v[m],r=e[s]||Hs,h=l(s),Is.test(h)&&(f={name:s,path:h,options:r,mode:Ls.ONE_WAY,raw:null},o=u(s),null===(a=K(t,o))&&(null!==(a=K(t,o+".sync"))?f.mode=Ls.TWO_WAY:null!==(a=K(t,o+".once"))&&(f.mode=Ls.ONE_TIME)),null!==a?(f.raw=a,c=I(a),a=c.expression,f.filters=c.filters,n(a)&&!c.filters?f.optimizedLiteral=!0:f.dynamic=!0,f.parentPath=a):null!==(a=Y(t,o))?f.raw=a:d&&null!==(a=d[s]||d[h])&&(f.raw=a),p.push(f));return Pe(p)}function Pe(t){return function(e,n){e._props={};for(var r,s,l,c,f,p=e.$options.propsData,d=t.length;d--;)if(r=t[d],f=r.raw,s=r.path,l=r.options,e._props[s]=r,p&&i(p,s)&&Le(e,r,p[s]),null===f)Le(e,r,void 0);else if(r.dynamic)r.mode===Ls.ONE_TIME?(c=(n||e._context||e).$get(r.parentPath),Le(e,r,c)):e._context?e._bindDir({name:"prop",def:Ws,prop:r},null,null,n):Le(e,r,e.$get(r.parentPath));else if(r.optimizedLiteral){var v=h(f);c=v===f?a(o(f)):v,Le(e,r,c)}else c=l.type===Boolean&&(""===f||f===u(r.name))||f,Le(e,r,c)}}function Re(t,e,i,n){var r=e.dynamic&&Kt(e.parentPath),s=i;void 0===s&&(s=Ie(t,e)),s=We(e,s,t);var o=s!==i;Me(e,s,t)||(s=void 0),r&&!o?St(function(){n(s)}):n(s)}function Le(t,e,i){Re(t,e,i,function(i){Lt(t,e.path,i)})}function He(t,e,i){Re(t,e,i,function(i){t[e.path]=i})}function Ie(t,e){var n=e.options;if(!i(n,"default"))return n.type!==Boolean&&void 0;var r=n.default;return m(r),"function"==typeof r&&n.type!==Function?r.call(t):r}function Me(t,e,i){if(!t.options.required&&(null===t.raw||null==e))return!0;var n=t.options,r=n.type,s=!r,o=[];if(r){qi(r)||(r=[r]);for(var a=0;a<r.length&&!s;a++){var h=Ve(e,r[a]);o.push(h.expectedType),s=h.valid}}if(!s)return!1;var l=n.validator;return!(l&&!l(e))}function We(t,e,i){var n=t.options.coerce;return n&&"function"==typeof n?n(e):e}function Ve(t,e){var i,n;return e===String?(n="string",i=typeof t===n):e===Number?(n="number",i=typeof t===n):e===Boolean?(n="boolean",i=typeof t===n):e===Function?(n="function",i=typeof t===n):e===Object?(n="object",i=g(t)):e===Array?(n="array",i=qi(t)):i=t instanceof e,{valid:i,expectedType:n}}function Be(t){Vs.push(t),Bs||(Bs=!0,ln(ze))}function ze(){for(var t=document.documentElement.offsetHeight,e=0;e<Vs.length;e++)Vs[e]();return Vs=[],Bs=!1,t}function Ue(t,e,i,n){this.id=e,this.el=t,this.enterClass=i&&i.enterClass||e+"-enter",this.leaveClass=i&&i.leaveClass||e+"-leave",this.hooks=i,this.vm=n,this.pendingCssEvent=this.pendingCssCb=this.cancel=this.pendingJsCb=this.op=this.cb=null,this.justEntered=!1,this.entered=this.left=!1,this.typeCache={},this.type=i&&i.type;var r=this;["enterNextTick","enterDone","leaveNextTick","leaveDone"].forEach(function(t){r[t]=p(r[t],r)})}function Je(t){if(/svg$/.test(t.namespaceURI)){var e=t.getBoundingClientRect();return!(e.width||e.height)}return!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)}function qe(t,e,i){var n=i||!e._asComponent?ti(t,e):null,r=n&&n.terminal||gi(t)||!t.hasChildNodes()?null:oi(t.childNodes,e);return function(t,e,i,s,o){var a=d(e.childNodes),h=Qe(function(){n&&n(t,e,i,s,o),r&&r(t,a,i,s,o)},t);return Ze(t,h)}}function Qe(t,e){e._directives=[];var i=e._directives.length;t();var n=e._directives.slice(i);Ge(n);for(var r=0,s=n.length;r<s;r++)n[r]._bind();return n}function Ge(t){if(0!==t.length){var e,i,n,r,s={},o=0,a=[];for(e=0,i=t.length;e<i;e++){var h=t[e],l=h.descriptor.def.priority||ro,c=s[l];c||(c=s[l]=[],a.push(l)),c.push(h)}for(a.sort(function(t,e){return t>e?-1:t===e?0:1}),e=0,i=a.length;e<i;e++){var u=s[a[e]];for(n=0,r=u.length;n<r;n++)t[o++]=u[n]}}}function Ze(t,e,i,n){function r(r){Xe(t,e,r),i&&n&&Xe(i,n)}return r.dirs=e,r}function Xe(t,e,i){for(var n=e.length;n--;)e[n]._teardown()}function Ye(t,e,i,n){var r=De(e,i,t),s=Qe(function(){r(t,n)},t);return Ze(t,s)}function Ke(t,e,i){var n,r,s=e._containerAttrs,o=e._replacerAttrs;return 11!==t.nodeType&&(e._asComponent?(s&&i&&(n=pi(s,i)),o&&(r=pi(o,e))):r=pi(t.attributes,e)),e._containerAttrs=e._replacerAttrs=null,function(t,e,i){var s,o=t._context;o&&n&&(s=Qe(function(){n(o,e,null,i)},o));var a=Qe(function(){r&&r(t,e)},t);return Ze(t,a,o,s)}}function ti(t,e){var i=t.nodeType;return 1!==i||gi(t)?3===i&&t.data.trim()?ii(t,e):null:ei(t,e)}function ei(t,e){if("TEXTAREA"===t.tagName){if(null!==Y(t,"v-pre"))return ui;var i=V(t.value);i&&(t.setAttribute(":value",B(i)),t.value="")}var n,r=t.hasAttributes(),s=r&&d(t.attributes);return r&&(n=ci(t,s,e)),n||(n=hi(t,e)),n||(n=li(t,e)),!n&&r&&(n=pi(s,e)),n}function ii(t,e){if(t._skip)return ni;var i=V(t.wholeText);if(!i)return null;for(var n=t.nextSibling;n&&3===n.nodeType;)n._skip=!0,n=n.nextSibling;for(var r,s,o=document.createDocumentFragment(),a=0,h=i.length;a<h;a++)s=i[a],r=s.tag?ri(s,e):document.createTextNode(s.value),o.appendChild(r);return si(i,o,e)}function ni(t,e){nt(e)}function ri(t,e){function i(e){if(!t.descriptor){var i=I(t.value);t.descriptor={name:e,def:Ds[e],expression:i.expression,filters:i.filters}}}var n;return t.oneTime?n=document.createTextNode(t.value):t.html?(n=document.createComment("v-html"),i("html")):(n=document.createTextNode(" "),i("text")),n}function si(t,e){return function(i,n,r,o){for(var a,h,l,c=e.cloneNode(!0),u=d(c.childNodes),f=0,p=t.length;f<p;f++)a=t[f],h=a.value,a.tag&&(l=u[f],a.oneTime?(h=(o||i).$eval(h),a.html?st(l,ce(h,!0)):l.data=s(h)):i._bindDir(a.descriptor,l,r,o));st(n,c)}}function oi(t,e){for(var i,n,r,s=[],o=0,a=t.length;o<a;o++)r=t[o],i=ti(r,e),n=i&&i.terminal||"SCRIPT"===r.tagName||!r.hasChildNodes()?null:oi(r.childNodes,e),s.push(i,n);return s.length?ai(s):null}function ai(t){return function(e,i,n,r,s){for(var o,a,h,l=0,c=0,u=t.length;l<u;c++){o=i[c],a=t[l++],h=t[l++];var f=d(o.childNodes);a&&a(e,o,n,r,s),h&&h(e,f,n,r,s)}}}function hi(t,e){var i=t.tagName.toLowerCase();if(!zn.test(i)){var n=jt(e,"elementDirectives",i);return n?fi(t,i,"",e,n):void 0}}function li(t,e){var i=Ct(t,e);if(i){var n=gt(t),r={name:"component",ref:n,expression:i.id,def:Ys.component,modifiers:{literal:!i.dynamic}},s=function(t,e,i,s,o){n&&Lt((s||t).$refs,n,null),t._bindDir(r,e,i,s,o)};return s.terminal=!0,s}}function ci(t,e,i){if(null!==Y(t,"v-pre"))return ui;if(t.hasAttribute("v-else")){var n=t.previousElementSibling;if(n&&n.hasAttribute("v-if"))return ui}for(var r,s,o,a,h,l,c,u,f,p,d=0,v=e.length;d<v;d++)r=e[d],s=r.name.replace(io,""),(h=s.match(eo))&&(f=jt(i,"directives",h[1]),f&&f.terminal&&(!p||(f.priority||so)>p.priority)&&(p=f,c=r.name,a=di(r.name),o=r.value,l=h[1],u=h[2]));return p?fi(t,l,o,i,p,c,u,a):void 0}function ui(){}function fi(t,e,i,n,r,s,o,a){var h=I(i),l={name:e,arg:o,expression:h.expression,filters:h.filters,raw:i,attr:s,modifiers:a,def:r};"for"!==e&&"router-view"!==e||(l.ref=gt(t));var c=function(t,e,i,n,r){l.ref&&Lt((n||t).$refs,l.ref,null),t._bindDir(l,e,i,n,r)};return c.terminal=!0,c}function pi(t,e){function i(t,e,i){var n=i&&mi(i),r=!n&&I(s);v.push({name:t,attr:o,raw:a,def:e,arg:l,modifiers:c,expression:r&&r.expression,filters:r&&r.filters,interp:i,hasOneTime:n})}for(var n,r,s,o,a,h,l,c,u,f,p,d=t.length,v=[];d--;)if(n=t[d],r=o=n.name,s=a=n.value,f=V(s),l=null,c=di(r),r=r.replace(io,""),f)s=B(f),l=r,i("bind",Ds.bind,f);else if(no.test(r))c.literal=!Ks.test(r),i("transition",Ys.transition);else if(to.test(r))l=r.replace(to,""),i("on",Ds.on);else if(Ks.test(r))h=r.replace(Ks,""),"style"===h||"class"===h?i(h,Ys[h]):(l=h,i("bind",Ds.bind));else if(p=r.match(eo)){if(h=p[1],l=p[2],"else"===h)continue;u=jt(e,"directives",h,!0),u&&i(h,u)}if(v.length)return vi(v)}function di(t){var e=Object.create(null),i=t.match(io);if(i)for(var n=i.length;n--;)e[i[n].slice(1)]=!0;return e}function vi(t){return function(e,i,n,r,s){for(var o=t.length;o--;)e._bindDir(t[o],i,n,r,s)}}function mi(t){for(var e=t.length;e--;)if(t[e].oneTime)return!0}function gi(t){return"SCRIPT"===t.tagName&&(!t.hasAttribute("type")||"text/javascript"===t.getAttribute("type"))}function _i(t,e){return e&&(e._containerAttrs=bi(t)),vt(t)&&(t=ce(t)),e&&(e._asComponent&&!e.template&&(e.template="<slot></slot>"),e.template&&(e._content=ft(t),t=yi(t,e))),bt(t)&&(rt(mt("v-start",!0),t),t.appendChild(mt("v-end",!0))),t}function yi(t,e){var i=e.template,n=ce(i,!0);if(n){var r=n.firstChild;if(!r)return n;var s=r.tagName&&r.tagName.toLowerCase();return e.replace?(t===document.body,n.childNodes.length>1||1!==r.nodeType||"component"===s||jt(e,"components",s)||tt(r,"is")||jt(e,"elementDirectives",s)||r.hasAttribute("v-for")||r.hasAttribute("v-if")?n:(e._replacerAttrs=bi(r),wi(t,r),r)):(t.appendChild(n),t)}}function bi(t){if(1===t.nodeType&&t.hasAttributes())return d(t.attributes)}function wi(t,e){for(var i,n,r=t.attributes,s=r.length;s--;)i=r[s].name,n=r[s].value,e.hasAttribute(i)||oo.test(i)?"class"===i&&!V(n)&&(n=n.trim())&&n.split(/\s+/).forEach(function(t){ct(e,t)}):e.setAttribute(i,n)}function Ci(t,e){if(e){for(var i,n,r=t._slotContents=Object.create(null),s=0,o=e.children.length;s<o;s++)i=e.children[s],(n=i.getAttribute("slot"))&&(r[n]||(r[n]=[])).push(i);for(n in r)r[n]=$i(r[n],e);if(e.hasChildNodes()){var a=e.childNodes;if(1===a.length&&3===a[0].nodeType&&!a[0].data.trim())return;r.default=$i(e.childNodes,e)}}}function $i(t,e){var i=document.createDocumentFragment();t=d(t);for(var n=0,r=t.length;n<r;n++){var s=t[n];!vt(s)||s.hasAttribute("v-if")||s.hasAttribute("v-for")||(e.removeChild(s),s=ce(s,!0)),i.appendChild(s)}return i}function ki(t){function e(){}function n(t,e){var i=new re(e,t,null,{lazy:!0});return function(){return i.dirty&&i.evaluate(),Et.target&&i.depend(),i.value}}Object.defineProperty(t.prototype,"$data",{get:function(){return this._data},set:function(t){t!==this._data&&this._setData(t)}}),t.prototype._initState=function(){this._initProps(),this._initMeta(),this._initMethods(),this._initData(),this._initComputed()},t.prototype._initProps=function(){var t=this.$options,e=t.el,i=t.props;e=t.el=Z(e),this._propsUnlinkFn=e&&1===e.nodeType&&i?Ye(this,e,i,this._scope):null},t.prototype._initData=function(){var t=this.$options.data,e=this._data=t?t():{};g(e)||(e={});var n,r,s=this._props,o=Object.keys(e);for(n=o.length;n--;)r=o[n],s&&i(s,r)||this._proxy(r);Rt(e,this)},t.prototype._setData=function(t){t=t||{};var e=this._data;this._data=t;var n,r,s;for(n=Object.keys(e),s=n.length;s--;)r=n[s],r in t||this._unproxy(r);for(n=Object.keys(t),s=n.length;s--;)r=n[s],i(this,r)||this._proxy(r);e.__ob__.removeVm(this),Rt(t,this),this._digest()},t.prototype._proxy=function(t){if(!r(t)){var e=this;Object.defineProperty(e,t,{configurable:!0,enumerable:!0,get:function(){return e._data[t]},set:function(i){e._data[t]=i}})}},t.prototype._unproxy=function(t){r(t)||delete this[t]},t.prototype._digest=function(){for(var t=0,e=this._watchers.length;t<e;t++)this._watchers[t].update(!0)},t.prototype._initComputed=function(){var t=this.$options.computed;if(t)for(var i in t){var r=t[i],s={enumerable:!0,configurable:!0};"function"==typeof r?(s.get=n(r,this),s.set=e):(s.get=r.get?r.cache!==!1?n(r.get,this):p(r.get,this):e,s.set=r.set?p(r.set,this):e),Object.defineProperty(this,i,s)}},t.prototype._initMethods=function(){var t=this.$options.methods;if(t)for(var e in t)this[e]=p(t[e],this)},t.prototype._initMeta=function(){var t=this.$options._meta;if(t)for(var e in t)Lt(this,e,t[e])}}function xi(t){function e(t,e){for(var i,n,r,s=e.attributes,o=0,a=s.length;o<a;o++)i=s[o].name,ho.test(i)&&(i=i.replace(ho,""),n=s[o].value,Kt(n)&&(n+=".apply(this, $arguments)"),r=(t._scope||t._context).$eval(n,!0),r._fromParent=!0,t.$on(i.replace(ho),r))}function i(t,e,i){if(i){var r,s,o,a;for(s in i)if(r=i[s],qi(r))for(o=0,a=r.length;o<a;o++)n(t,e,s,r[o]);else n(t,e,s,r)}}function n(t,e,i,r,s){var o=typeof r;if("function"===o)t[e](i,r,s);else if("string"===o){var a=t.$options.methods,h=a&&a[r];h&&t[e](i,h,s)}else r&&"object"===o&&n(t,e,i,r.handler,r)}function r(){this._isAttached||(this._isAttached=!0,this.$children.forEach(s))}function s(t){!t._isAttached&&X(t.$el)&&t._callHook("attached")}function o(){this._isAttached&&(this._isAttached=!1,this.$children.forEach(a))}function a(t){t._isAttached&&!X(t.$el)&&t._callHook("detached")}t.prototype._initEvents=function(){var t=this.$options;t._asComponent&&e(this,t.el),i(this,"$on",t.events),i(this,"$watch",t.watch)},t.prototype._initDOMHooks=function(){this.$on("hook:attached",r),this.$on("hook:detached",o)},t.prototype._callHook=function(t){this.$emit("pre-hook:"+t);var e=this.$options[t];if(e)for(var i=0,n=e.length;i<n;i++)e[i].call(this);this.$emit("hook:"+t)}}function Ai(){}function Oi(t,e,i,n,r,s){this.vm=e,this.el=i,this.descriptor=t,this.name=t.name,this.expression=t.expression,this.arg=t.arg,this.modifiers=t.modifiers,this.filters=t.filters,this.literal=this.modifiers&&this.modifiers.literal,this._locked=!1,this._bound=!1,this._listeners=null,this._host=n,this._scope=r,this._frag=s}function Ti(t){t.prototype._updateRef=function(t){var e=this.$options._ref;if(e){var i=(this._scope||this._context).$refs;t?i[e]===this&&(i[e]=null):i[e]=this}},t.prototype._compile=function(t){var e=this.$options,i=t;if(t=_i(t,e),this._initElement(t),1!==t.nodeType||null===Y(t,"v-pre")){var n=this._context&&this._context.$options,r=Ke(t,e,n);Ci(this,e._content);var s,o=this.constructor;e._linkerCachable&&(s=o.linker,s||(s=o.linker=qe(t,e)));var a=r(this,t,this._scope),h=s?s(this,t):qe(t,e)(this,t);
+this._unlinkFn=function(){a(),h(!0)},e.replace&&st(i,t),this._isCompiled=!0,this._callHook("compiled")}},t.prototype._initElement=function(t){bt(t)?(this._isFragment=!0,this.$el=this._fragmentStart=t.firstChild,this._fragmentEnd=t.lastChild,3===this._fragmentStart.nodeType&&(this._fragmentStart.data=this._fragmentEnd.data=""),this._fragment=t):this.$el=t,this.$el.__vue__=this,this._callHook("beforeCompile")},t.prototype._bindDir=function(t,e,i,n,r){this._directives.push(new Oi(t,this,e,i,n,r))},t.prototype._destroy=function(t,e){if(this._isBeingDestroyed)return void(e||this._cleanup());var i,n,r=this,s=function(){!i||n||e||r._cleanup()};t&&this.$el&&(n=!0,this.$remove(function(){n=!1,s()})),this._callHook("beforeDestroy"),this._isBeingDestroyed=!0;var o,a=this.$parent;for(a&&!a._isBeingDestroyed&&(a.$children.$remove(this),this._updateRef(!0)),o=this.$children.length;o--;)this.$children[o].$destroy();for(this._propsUnlinkFn&&this._propsUnlinkFn(),this._unlinkFn&&this._unlinkFn(),o=this._watchers.length;o--;)this._watchers[o].teardown();this.$el&&(this.$el.__vue__=null),i=!0,s()},t.prototype._cleanup=function(){this._isDestroyed||(this._frag&&this._frag.children.$remove(this),this._data&&this._data.__ob__&&this._data.__ob__.removeVm(this),this.$el=this.$parent=this.$root=this.$children=this._watchers=this._context=this._scope=this._directives=null,this._isDestroyed=!0,this._callHook("destroyed"),this.$off())}}function Ni(t){t.prototype._applyFilters=function(t,e,i,n){var r,s,o,a,h,l,c,u,f;for(l=0,c=i.length;l<c;l++)if(r=i[n?c-l-1:l],s=jt(this.$options,"filters",r.name,!0),s&&(s=n?s.write:s.read||s,"function"==typeof s)){if(o=n?[t,e]:[t],h=n?2:1,r.args)for(u=0,f=r.args.length;u<f;u++)a=r.args[u],o[u+h]=a.dynamic?this.$get(a.value):a.value;t=s.apply(this,o)}return t},t.prototype._resolveComponent=function(e,i){var n;if(n="function"==typeof e?e:jt(this.$options,"components",e,!0))if(n.options)i(n);else if(n.resolved)i(n.resolved);else if(n.requested)n.pendingCallbacks.push(i);else{n.requested=!0;var r=n.pendingCallbacks=[i];n.call(this,function(e){g(e)&&(e=t.extend(e)),n.resolved=e;for(var i=0,s=r.length;i<s;i++)r[i](e)},function(t){})}}}function ji(t){function i(t){return JSON.parse(JSON.stringify(t))}t.prototype.$get=function(t,e){var i=Yt(t);if(i){if(e){var n=this;return function(){n.$arguments=d(arguments);var t=i.get.call(n,n);return n.$arguments=null,t}}try{return i.get.call(this,this)}catch(t){}}},t.prototype.$set=function(t,e){var i=Yt(t,!0);i&&i.set&&i.set.call(this,this,e)},t.prototype.$delete=function(t){e(this._data,t)},t.prototype.$watch=function(t,e,i){var n,r=this;"string"==typeof t&&(n=I(t),t=n.expression);var s=new re(r,t,e,{deep:i&&i.deep,sync:i&&i.sync,filters:n&&n.filters,user:!i||i.user!==!1});return i&&i.immediate&&e.call(r,s.value),function(){s.teardown()}},t.prototype.$eval=function(t,e){if(lo.test(t)){var i=I(t),n=this.$get(i.expression,e);return i.filters?this._applyFilters(n,null,i.filters):n}return this.$get(t,e)},t.prototype.$interpolate=function(t){var e=V(t),i=this;return e?1===e.length?i.$eval(e[0].value)+"":e.map(function(t){return t.tag?i.$eval(t.value):t.value}).join(""):t},t.prototype.$log=function(t){var e=t?Bt(this._data,t):this._data;if(e&&(e=i(e)),!t){var n;for(n in this.$options.computed)e[n]=i(this[n]);if(this._props)for(n in this._props)e[n]=i(this[n])}console.log(e)}}function Ei(t){function e(t,e,n,r,s,o){e=i(e);var a=!X(e),h=r===!1||a?s:o,l=!a&&!t._isAttached&&!X(t.$el);return t._isFragment?(_t(t._fragmentStart,t._fragmentEnd,function(i){h(i,e,t)}),n&&n()):h(t.$el,e,t,n),l&&t._callHook("attached"),t}function i(t){return"string"==typeof t?document.querySelector(t):t}function n(t,e,i,n){e.appendChild(t),n&&n()}function r(t,e,i,n){et(t,e),n&&n()}function s(t,e,i){nt(t),i&&i()}t.prototype.$nextTick=function(t){ln(t,this)},t.prototype.$appendTo=function(t,i,r){return e(this,t,i,r,n,J)},t.prototype.$prependTo=function(t,e,n){return t=i(t),t.hasChildNodes()?this.$before(t.firstChild,e,n):this.$appendTo(t,e,n),this},t.prototype.$before=function(t,i,n){return e(this,t,i,n,r,q)},t.prototype.$after=function(t,e,n){return t=i(t),t.nextSibling?this.$before(t.nextSibling,e,n):this.$appendTo(t.parentNode,e,n),this},t.prototype.$remove=function(t,e){if(!this.$el.parentNode)return t&&t();var i=this._isAttached&&X(this.$el);i||(e=!1);var n=this,r=function(){i&&n._callHook("detached"),t&&t()};if(this._isFragment)yt(this._fragmentStart,this._fragmentEnd,this,this._fragment,r);else{var o=e===!1?s:Q;o(this.$el,this,r)}return this}}function Si(t){function e(t,e,n){var r=t.$parent;if(r&&n&&!i.test(e))for(;r;)r._eventsCount[e]=(r._eventsCount[e]||0)+n,r=r.$parent}t.prototype.$on=function(t,i){return(this._events[t]||(this._events[t]=[])).push(i),e(this,t,1),this},t.prototype.$once=function(t,e){function i(){n.$off(t,i),e.apply(this,arguments)}var n=this;return i.fn=e,this.$on(t,i),this},t.prototype.$off=function(t,i){var n;if(!arguments.length){if(this.$parent)for(t in this._events)n=this._events[t],n&&e(this,t,-n.length);return this._events={},this}if(n=this._events[t],!n)return this;if(1===arguments.length)return e(this,t,-n.length),this._events[t]=null,this;for(var r,s=n.length;s--;)if(r=n[s],r===i||r.fn===i){e(this,t,-1),n.splice(s,1);break}return this},t.prototype.$emit=function(t){var e="string"==typeof t;t=e?t:t.name;var i=this._events[t],n=e||!i;if(i){i=i.length>1?d(i):i;var r=e&&i.some(function(t){return t._fromParent});r&&(n=!1);for(var s=d(arguments,1),o=0,a=i.length;o<a;o++){var h=i[o],l=h.apply(this,s);l!==!0||r&&!h._fromParent||(n=!0)}}return n},t.prototype.$broadcast=function(t){var e="string"==typeof t;if(t=e?t:t.name,this._eventsCount[t]){var i=this.$children,n=d(arguments);e&&(n[0]={name:t,source:this});for(var r=0,s=i.length;r<s;r++){var o=i[r],a=o.$emit.apply(o,n);a&&o.$broadcast.apply(o,n)}return this}},t.prototype.$dispatch=function(t){var e=this.$emit.apply(this,arguments);if(e){var i=this.$parent,n=d(arguments);for(n[0]={name:t,source:this};i;)e=i.$emit.apply(i,n),i=e?i.$parent:null;return this}};var i=/^hook:/}function Fi(t){function e(){this._isAttached=!0,this._isReady=!0,this._callHook("ready")}t.prototype.$mount=function(t){if(!this._isCompiled)return t=Z(t),t||(t=document.createElement("div")),this._compile(t),this._initDOMHooks(),X(this.$el)?(this._callHook("attached"),e.call(this)):this.$once("hook:attached",e),this},t.prototype.$destroy=function(t,e){this._destroy(t,e)},t.prototype.$compile=function(t,e,i,n){return qe(t,this.$options,!0)(this,t,e,i,n)}}function Di(t){this._init(t)}function Pi(t,e,i){return i=i?parseInt(i,10):0,e=o(e),"number"==typeof e?t.slice(i,i+e):t}function Ri(t,e,i){if(t=po(t),null==e)return t;if("function"==typeof e)return t.filter(e);e=(""+e).toLowerCase();for(var n,r,s,o,a="in"===i?3:2,h=Array.prototype.concat.apply([],d(arguments,a)),l=[],c=0,u=t.length;c<u;c++)if(n=t[c],s=n&&n.$value||n,o=h.length){for(;o--;)if(r=h[o],"$key"===r&&Hi(n.$key,e)||Hi(Bt(s,r),e)){l.push(n);break}}else Hi(n,e)&&l.push(n);return l}function Li(t){function e(t,e,i){var r=n[i];return r&&("$key"!==r&&(m(t)&&"$value"in t&&(t=t.$value),m(e)&&"$value"in e&&(e=e.$value)),t=m(t)?Bt(t,r):t,e=m(e)?Bt(e,r):e),t===e?0:t>e?s:-s}var i=null,n=void 0;t=po(t);var r=d(arguments,1),s=r[r.length-1];"number"==typeof s?(s=s<0?-1:1,r=r.length>1?r.slice(0,-1):r):s=1;var o=r[0];return o?("function"==typeof o?i=function(t,e){return o(t,e)*s}:(n=Array.prototype.concat.apply([],r),i=function(t,r,s){return s=s||0,s>=n.length-1?e(t,r,s):e(t,r,s)||i(t,r,s+1)}),t.slice().sort(i)):t}function Hi(t,e){var i;if(g(t)){var n=Object.keys(t);for(i=n.length;i--;)if(Hi(t[n[i]],e))return!0}else if(qi(t)){for(i=t.length;i--;)if(Hi(t[i],e))return!0}else if(null!=t)return t.toString().toLowerCase().indexOf(e)>-1}function Ii(i){function n(t){return new Function("return function "+f(t)+" (options) { this._init(options) }")()}i.options={directives:Ds,elementDirectives:fo,filters:mo,transitions:{},components:{},partials:{},replace:!0},i.util=Kn,i.config=Mn,i.set=t,i.delete=e,i.nextTick=ln,i.compiler=ao,i.FragmentFactory=_e,i.internalDirectives=Ys,i.parsers={path:mr,text:Ln,template:qr,directive:En,expression:jr},i.cid=0;var r=1;i.extend=function(t){t=t||{};var e=this,i=0===e.cid;if(i&&t._Ctor)return t._Ctor;var s=t.name||e.options.name,o=n(s||"VueComponent");return o.prototype=Object.create(e.prototype),o.prototype.constructor=o,o.cid=r++,o.options=Nt(e.options,t),o.super=e,o.extend=e.extend,Mn._assetTypes.forEach(function(t){o[t]=e[t]}),s&&(o.options.components[s]=o),i&&(t._Ctor=o),o},i.use=function(t){if(!t.installed){var e=d(arguments,1);return e.unshift(this),"function"==typeof t.install?t.install.apply(t,e):t.apply(null,e),t.installed=!0,this}},i.mixin=function(t){i.options=Nt(i.options,t)},Mn._assetTypes.forEach(function(t){i[t]=function(e,n){return n?("component"===t&&g(n)&&(n.name||(n.name=e),n=i.extend(n)),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}}),v(i.transition,Vn)}var Mi=Object.prototype.hasOwnProperty,Wi=/^\s?(true|false|-?[\d\.]+|'[^']*'|"[^"]*")\s?$/,Vi=/-(\w)/g,Bi=/([^-])([A-Z])/g,zi=/(?:^|[-_\/])(\w)/g,Ui=Object.prototype.toString,Ji="[object Object]",qi=Array.isArray,Qi="__proto__"in{},Gi="undefined"!=typeof window&&"[object Object]"!==Object.prototype.toString.call(window),Zi=Gi&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__,Xi=Gi&&window.navigator.userAgent.toLowerCase(),Yi=Xi&&Xi.indexOf("trident")>0,Ki=Xi&&Xi.indexOf("msie 9.0")>0,tn=Xi&&Xi.indexOf("android")>0,en=Xi&&/iphone|ipad|ipod|ios/.test(Xi),nn=void 0,rn=void 0,sn=void 0,on=void 0;if(Gi&&!Ki){var an=void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend,hn=void 0===window.onanimationend&&void 0!==window.onwebkitanimationend;nn=an?"WebkitTransition":"transition",rn=an?"webkitTransitionEnd":"transitionend",sn=hn?"WebkitAnimation":"animation",on=hn?"webkitAnimationEnd":"animationend"}var ln=function(){function t(){i=!1;var t=e.slice(0);e.length=0;for(var n=0;n<t.length;n++)t[n]()}var e=[],i=!1,n=void 0;if("undefined"!=typeof Promise&&$(Promise)){var r=Promise.resolve(),s=function(){};n=function(){r.then(t),en&&setTimeout(s)}}else if("undefined"!=typeof MutationObserver){var o=1,a=new MutationObserver(t),h=document.createTextNode(String(o));a.observe(h,{characterData:!0}),n=function(){o=(o+1)%2,h.data=String(o)}}else n=setTimeout;return function(r,s){var o=s?function(){r.call(s)}:r;e.push(o),i||(i=!0,n(t,0))}}(),cn=void 0;"undefined"!=typeof Set&&$(Set)?cn=Set:(cn=function(){this.set=Object.create(null)},cn.prototype.has=function(t){return void 0!==this.set[t]},cn.prototype.add=function(t){this.set[t]=1},cn.prototype.clear=function(){this.set=Object.create(null)});var un=k.prototype;un.put=function(t,e){var i,n=this.get(t,!0);return n||(this.size===this.limit&&(i=this.shift()),n={key:t},this._keymap[t]=n,this.tail?(this.tail.newer=n,n.older=this.tail):this.head=n,this.tail=n,this.size++),n.value=e,i},un.shift=function(){var t=this.head;return t&&(this.head=this.head.newer,this.head.older=void 0,t.newer=t.older=void 0,this._keymap[t.key]=void 0,this.size--),t},un.get=function(t,e){var i=this._keymap[t];if(void 0!==i)return i===this.tail?e?i:i.value:(i.newer&&(i===this.head&&(this.head=i.newer),i.newer.older=i.older),i.older&&(i.older.newer=i.newer),i.newer=void 0,i.older=this.tail,this.tail&&(this.tail.newer=i),this.tail=i,e?i:i.value)};var fn,pn,dn,vn,mn,gn,_n=new k(1e3),yn=/^in$|^-?\d+/,bn=0,wn=1,Cn=2,$n=3,kn=34,xn=39,An=124,On=92,Tn=32,Nn={91:1,123:1,40:1},jn={91:93,123:125,40:41},En=Object.freeze({parseDirective:I}),Sn=/[-.*+?^${}()|[\]\/\\]/g,Fn=void 0,Dn=void 0,Pn=void 0,Rn=/[^|]\|[^|]/,Ln=Object.freeze({compileRegex:W,parseText:V,tokensToExp:B}),Hn=["{{","}}"],In=["{{{","}}}"],Mn=Object.defineProperties({debug:!1,silent:!1,async:!0,warnExpressionErrors:!0,devtools:!1,_delimitersChanged:!0,_assetTypes:["component","directive","elementDirective","filter","transition","partial"],_propBindingModes:{ONE_WAY:0,TWO_WAY:1,ONE_TIME:2},_maxUpdateCount:100},{delimiters:{get:function(){return Hn},set:function(t){Hn=t,W()},configurable:!0,enumerable:!0},unsafeDelimiters:{get:function(){return In},set:function(t){In=t,W()},configurable:!0,enumerable:!0}}),Wn=void 0,Vn=Object.freeze({appendWithTransition:J,beforeWithTransition:q,removeWithTransition:Q,applyTransition:G}),Bn=/^v-ref:/,zn=/^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/i,Un=/^(slot|partial|component)$/i,Jn=Mn.optionMergeStrategies=Object.create(null);Jn.data=function(t,e,i){return i?t||e?function(){var n="function"==typeof e?e.call(i):e,r="function"==typeof t?t.call(i):void 0;return n?kt(n,r):r}:void 0:e?"function"!=typeof e?t:t?function(){return kt(e.call(this),t.call(this))}:e:t},Jn.el=function(t,e,i){if(i||!e||"function"==typeof e){var n=e||t;return i&&"function"==typeof n?n.call(i):n}},Jn.init=Jn.created=Jn.ready=Jn.attached=Jn.detached=Jn.beforeCompile=Jn.compiled=Jn.beforeDestroy=Jn.destroyed=Jn.activate=function(t,e){return e?t?t.concat(e):qi(e)?e:[e]:t},Mn._assetTypes.forEach(function(t){Jn[t+"s"]=xt}),Jn.watch=Jn.events=function(t,e){if(!e)return t;if(!t)return e;var i={};v(i,t);for(var n in e){var r=i[n],s=e[n];r&&!qi(r)&&(r=[r]),i[n]=r?r.concat(s):[s]}return i},Jn.props=Jn.methods=Jn.computed=function(t,e){if(!e)return t;if(!t)return e;var i=Object.create(null);return v(i,t),v(i,e),i};var qn=function(t,e){return void 0===e?t:e},Qn=0;Et.target=null,Et.prototype.addSub=function(t){this.subs.push(t)},Et.prototype.removeSub=function(t){this.subs.$remove(t)},Et.prototype.depend=function(){Et.target.addDep(this)},Et.prototype.notify=function(){for(var t=d(this.subs),e=0,i=t.length;e<i;e++)t[e].update()};var Gn=Array.prototype,Zn=Object.create(Gn);["push","pop","shift","unshift","splice","sort","reverse"].forEach(function(t){var e=Gn[t];_(Zn,t,function(){for(var i=arguments.length,n=new Array(i);i--;)n[i]=arguments[i];var r,s=e.apply(this,n),o=this.__ob__;switch(t){case"push":r=n;break;case"unshift":r=n;break;case"splice":r=n.slice(2)}return r&&o.observeArray(r),o.dep.notify(),s})}),_(Gn,"$set",function(t,e){return t>=this.length&&(this.length=Number(t)+1),this.splice(t,1,e)[0]}),_(Gn,"$remove",function(t){if(this.length){var e=b(this,t);return e>-1?this.splice(e,1):void 0}});var Xn=Object.getOwnPropertyNames(Zn),Yn=!0;Ft.prototype.walk=function(t){for(var e=Object.keys(t),i=0,n=e.length;i<n;i++)this.convert(e[i],t[e[i]])},Ft.prototype.observeArray=function(t){for(var e=0,i=t.length;e<i;e++)Rt(t[e])},Ft.prototype.convert=function(t,e){Lt(this.value,t,e)},Ft.prototype.addVm=function(t){(this.vms||(this.vms=[])).push(t)},Ft.prototype.removeVm=function(t){this.vms.$remove(t)};var Kn=Object.freeze({defineReactive:Lt,set:t,del:e,hasOwn:i,isLiteral:n,isReserved:r,_toString:s,toNumber:o,toBoolean:a,stripQuotes:h,camelize:l,hyphenate:u,classify:f,bind:p,toArray:d,extend:v,isObject:m,isPlainObject:g,def:_,debounce:y,indexOf:b,cancellable:w,looseEqual:C,isArray:qi,hasProto:Qi,inBrowser:Gi,devtools:Zi,isIE:Yi,isIE9:Ki,isAndroid:tn,isIOS:en,get transitionProp(){return nn},get transitionEndEvent(){return rn},get animationProp(){return sn},get animationEndEvent(){return on},nextTick:ln,get _Set(){return cn},query:Z,inDoc:X,getAttr:Y,getBindAttr:K,hasBindAttr:tt,before:et,after:it,remove:nt,prepend:rt,replace:st,on:ot,off:at,setClass:lt,addClass:ct,removeClass:ut,extractContent:ft,trimNode:pt,isTemplate:vt,createAnchor:mt,findRef:gt,mapNodeRange:_t,removeNodeRange:yt,isFragment:bt,getOuterHTML:wt,mergeOptions:Nt,resolveAsset:jt,checkComponentAttr:Ct,commonTagRE:zn,reservedTagRE:Un,warn:Wn}),tr=0,er=new k(1e3),ir=0,nr=1,rr=2,sr=3,or=0,ar=1,hr=2,lr=3,cr=4,ur=5,fr=6,pr=7,dr=8,vr=[];vr[or]={ws:[or],ident:[lr,ir],"[":[cr],eof:[pr]},vr[ar]={ws:[ar],".":[hr],"[":[cr],eof:[pr]},vr[hr]={ws:[hr],ident:[lr,ir]},vr[lr]={ident:[lr,ir],0:[lr,ir],number:[lr,ir],ws:[ar,nr],".":[hr,nr],"[":[cr,nr],eof:[pr,nr]},vr[cr]={"'":[ur,ir],'"':[fr,ir],"[":[cr,rr],"]":[ar,sr],eof:dr,else:[cr,ir]},vr[ur]={"'":[cr,ir],eof:dr,else:[ur,ir]},vr[fr]={'"':[cr,ir],eof:dr,else:[fr,ir]};var mr=Object.freeze({parsePath:Vt,getPath:Bt,setPath:zt}),gr=new k(1e3),_r="Math,Date,this,true,false,null,undefined,Infinity,NaN,isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,parseInt,parseFloat",yr=new RegExp("^("+_r.replace(/,/g,"\\b|")+"\\b)"),br="break,case,class,catch,const,continue,debugger,default,delete,do,else,export,extends,finally,for,function,if,import,in,instanceof,let,return,super,switch,throw,try,var,while,with,yield,enum,await,implements,package,protected,static,interface,private,public",wr=new RegExp("^("+br.replace(/,/g,"\\b|")+"\\b)"),Cr=/\s/g,$r=/\n/g,kr=/[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\"']|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g,xr=/"(\d+)"/g,Ar=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/,Or=/[^\w$\.](?:[A-Za-z_$][\w$]*)/g,Tr=/^(?:true|false|null|undefined|Infinity|NaN)$/,Nr=[],jr=Object.freeze({parseExpression:Yt,isSimplePath:Kt}),Er=[],Sr=[],Fr={},Dr={},Pr=!1,Rr=0;re.prototype.get=function(){this.beforeGet();var t,e=this.scope||this.vm;try{t=this.getter.call(e,e)}catch(t){}return this.deep&&se(t),this.preProcess&&(t=this.preProcess(t)),this.filters&&(t=e._applyFilters(t,null,this.filters,!1)),this.postProcess&&(t=this.postProcess(t)),this.afterGet(),t},re.prototype.set=function(t){var e=this.scope||this.vm;this.filters&&(t=e._applyFilters(t,this.value,this.filters,!0));try{this.setter.call(e,e,t)}catch(t){}var i=e.$forContext;if(i&&i.alias===this.expression){if(i.filters)return;i._withLock(function(){e.$key?i.rawValue[e.$key]=t:i.rawValue.$set(e.$index,t)})}},re.prototype.beforeGet=function(){Et.target=this},re.prototype.addDep=function(t){var e=t.id;this.newDepIds.has(e)||(this.newDepIds.add(e),this.newDeps.push(t),this.depIds.has(e)||t.addSub(this))},re.prototype.afterGet=function(){Et.target=null;for(var t=this.deps.length;t--;){var e=this.deps[t];this.newDepIds.has(e.id)||e.removeSub(this)}var i=this.depIds;this.depIds=this.newDepIds,this.newDepIds=i,this.newDepIds.clear(),i=this.deps,this.deps=this.newDeps,this.newDeps=i,this.newDeps.length=0},re.prototype.update=function(t){this.lazy?this.dirty=!0:this.sync||!Mn.async?this.run():(this.shallow=this.queued?!!t&&this.shallow:!!t,this.queued=!0,ne(this))},re.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||(m(t)||this.deep)&&!this.shallow){var e=this.value;this.value=t;this.prevError;this.cb.call(this.vm,t,e)}this.queued=this.shallow=!1}},re.prototype.evaluate=function(){var t=Et.target;this.value=this.get(),this.dirty=!1,Et.target=t},re.prototype.depend=function(){for(var t=this.deps.length;t--;)this.deps[t].depend()},re.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||this.vm._vForRemoving||this.vm._watchers.$remove(this);for(var t=this.deps.length;t--;)this.deps[t].removeSub(this);this.active=!1,this.vm=this.cb=this.value=null}};var Lr=new cn,Hr={bind:function(){this.attr=3===this.el.nodeType?"data":"textContent"},update:function(t){this.el[this.attr]=s(t)}},Ir=new k(1e3),Mr=new k(1e3),Wr={efault:[0,"",""],legend:[1,"<fieldset>","</fieldset>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]};Wr.td=Wr.th=[3,"<table><tbody><tr>","</tr></tbody></table>"],Wr.option=Wr.optgroup=[1,'<select multiple="multiple">',"</select>"],Wr.thead=Wr.tbody=Wr.colgroup=Wr.caption=Wr.tfoot=[1,"<table>","</table>"],Wr.g=Wr.defs=Wr.symbol=Wr.use=Wr.image=Wr.text=Wr.circle=Wr.ellipse=Wr.line=Wr.path=Wr.polygon=Wr.polyline=Wr.rect=[1,'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"version="1.1">',"</svg>"];var Vr=/<([\w:-]+)/,Br=/&#?\w+?;/,zr=/<!--/,Ur=function(){if(Gi){var t=document.createElement("div");return t.innerHTML="<template>1</template>",!t.cloneNode(!0).firstChild.innerHTML}return!1}(),Jr=function(){if(Gi){var t=document.createElement("textarea");return t.placeholder="t","t"===t.cloneNode(!0).value}return!1}(),qr=Object.freeze({cloneNode:le,parseTemplate:ce}),Qr={bind:function(){8===this.el.nodeType&&(this.nodes=[],this.anchor=mt("v-html"),st(this.el,this.anchor))},update:function(t){t=s(t),this.nodes?this.swap(t):this.el.innerHTML=t},swap:function(t){for(var e=this.nodes.length;e--;)nt(this.nodes[e]);var i=ce(t,!0,!0);this.nodes=d(i.childNodes),et(i,this.anchor)}};ue.prototype.callHook=function(t){var e,i;for(e=0,i=this.childFrags.length;e<i;e++)this.childFrags[e].callHook(t);for(e=0,i=this.children.length;e<i;e++)t(this.children[e])},ue.prototype.beforeRemove=function(){var t,e;for(t=0,e=this.childFrags.length;t<e;t++)this.childFrags[t].beforeRemove(!1);for(t=0,e=this.children.length;t<e;t++)this.children[t].$destroy(!1,!0);var i=this.unlink.dirs;for(t=0,e=i.length;t<e;t++)i[t]._watcher&&i[t]._watcher.teardown()},ue.prototype.destroy=function(){this.parentFrag&&this.parentFrag.childFrags.$remove(this),this.node.__v_frag=null,this.unlink()};var Gr=new k(5e3);_e.prototype.create=function(t,e,i){var n=le(this.template);return new ue(this.linker,this.vm,n,t,e,i)};var Zr=700,Xr=800,Yr=850,Kr=1100,ts=1500,es=1500,is=1750,ns=2100,rs=2200,ss=2300,os=0,as={priority:rs,terminal:!0,params:["track-by","stagger","enter-stagger","leave-stagger"],bind:function(){var t=this.expression.match(/(.*) (?:in|of) (.*)/);if(t){var e=t[1].match(/\((.*),(.*)\)/);e?(this.iterator=e[1].trim(),this.alias=e[2].trim()):this.alias=t[1].trim(),this.expression=t[2]}if(this.alias){this.id="__v-for__"+ ++os;var i=this.el.tagName;this.isOption=("OPTION"===i||"OPTGROUP"===i)&&"SELECT"===this.el.parentNode.tagName,this.start=mt("v-for-start"),this.end=mt("v-for-end"),st(this.el,this.end),et(this.start,this.end),this.cache=Object.create(null),this.factory=new _e(this.vm,this.el)}},update:function(t){this.diff(t),this.updateRef(),this.updateModel()},diff:function(t){var e,n,r,s,o,a,h=t[0],l=this.fromObject=m(h)&&i(h,"$key")&&i(h,"$value"),c=this.params.trackBy,u=this.frags,f=this.frags=new Array(t.length),p=this.alias,d=this.iterator,v=this.start,g=this.end,_=X(v),y=!u;for(e=0,n=t.length;e<n;e++)h=t[e],s=l?h.$key:null,o=l?h.$value:h,a=!m(o),r=!y&&this.getCachedFrag(o,e,s),r?(r.reused=!0,r.scope.$index=e,s&&(r.scope.$key=s),d&&(r.scope[d]=null!==s?s:e),(c||l||a)&&St(function(){r.scope[p]=o})):(r=this.create(o,p,e,s),r.fresh=!y),f[e]=r,y&&r.before(g);if(!y){var b=0,w=u.length-f.length;for(this.vm._vForRemoving=!0,e=0,n=u.length;e<n;e++)r=u[e],r.reused||(this.deleteCachedFrag(r),this.remove(r,b++,w,_));this.vm._vForRemoving=!1,b&&(this.vm._watchers=this.vm._watchers.filter(function(t){return t.active}));var C,$,k,x=0;for(e=0,n=f.length;e<n;e++)r=f[e],C=f[e-1],$=C?C.staggerCb?C.staggerAnchor:C.end||C.node:v,r.reused&&!r.staggerCb?(k=ye(r,v,this.id),k===C||k&&ye(k,v,this.id)===C||this.move(r,$)):this.insert(r,x++,$,_),r.reused=r.fresh=!1}},create:function(t,e,i,n){var r=this._host,s=this._scope||this.vm,o=Object.create(s);o.$refs=Object.create(s.$refs),o.$els=Object.create(s.$els),o.$parent=s,o.$forContext=this,St(function(){Lt(o,e,t)}),Lt(o,"$index",i),n?Lt(o,"$key",n):o.$key&&_(o,"$key",null),this.iterator&&Lt(o,this.iterator,null!==n?n:i);var a=this.factory.create(r,o,this._frag);return a.forId=this.id,this.cacheFrag(t,a,i,n),a},updateRef:function(){var t=this.descriptor.ref;if(t){var e,i=(this._scope||this.vm).$refs;this.fromObject?(e={},this.frags.forEach(function(t){e[t.scope.$key]=Ce(t)})):e=this.frags.map(Ce),i[t]=e}},updateModel:function(){if(this.isOption){var t=this.start.parentNode,e=t&&t.__v_model;e&&e.forceUpdate()}},insert:function(t,e,i,n){t.staggerCb&&(t.staggerCb.cancel(),t.staggerCb=null);var r=this.getStagger(t,e,null,"enter");if(n&&r){var s=t.staggerAnchor;s||(s=t.staggerAnchor=mt("stagger-anchor"),s.__v_frag=t),it(s,i);var o=t.staggerCb=w(function(){t.staggerCb=null,t.before(s),nt(s)});setTimeout(o,r)}else{var a=i.nextSibling;a||(it(this.end,i),a=this.end),t.before(a)}},remove:function(t,e,i,n){if(t.staggerCb)return t.staggerCb.cancel(),void(t.staggerCb=null);var r=this.getStagger(t,e,i,"leave");if(n&&r){var s=t.staggerCb=w(function(){t.staggerCb=null,t.remove()});setTimeout(s,r)}else t.remove()},move:function(t,e){e.nextSibling||this.end.parentNode.appendChild(this.end),t.before(e.nextSibling,!1)},cacheFrag:function(t,e,n,r){var s,o=this.params.trackBy,a=this.cache,h=!m(t);r||o||h?(s=we(n,r,t,o),a[s]||(a[s]=e)):(s=this.id,i(t,s)?null===t[s]&&(t[s]=e):Object.isExtensible(t)&&_(t,s,e)),e.raw=t},getCachedFrag:function(t,e,i){var n,r=this.params.trackBy,s=!m(t);if(i||r||s){var o=we(e,i,t,r);n=this.cache[o]}else n=t[this.id];return n&&(n.reused||n.fresh),n},deleteCachedFrag:function(t){var e=t.raw,n=this.params.trackBy,r=t.scope,s=r.$index,o=i(r,"$key")&&r.$key,a=!m(e);if(n||o||a){var h=we(s,o,e,n);this.cache[h]=null}else e[this.id]=null,t.raw=null},getStagger:function(t,e,i,n){n+="Stagger";var r=t.node.__v_trans,s=r&&r.hooks,o=s&&(s[n]||s.stagger);return o?o.call(t,e,i):e*parseInt(this.params[n]||this.params.stagger,10)},_preProcess:function(t){return this.rawValue=t,t},_postProcess:function(t){if(qi(t))return t;if(g(t)){for(var e,i=Object.keys(t),n=i.length,r=new Array(n);n--;)e=i[n],r[n]={$key:e,$value:t[e]};return r}return"number"!=typeof t||isNaN(t)||(t=be(t)),t||[]},unbind:function(){if(this.descriptor.ref&&((this._scope||this.vm).$refs[this.descriptor.ref]=null),this.frags)for(var t,e=this.frags.length;e--;)t=this.frags[e],this.deleteCachedFrag(t),t.destroy()}},hs={priority:ns,terminal:!0,bind:function(){var t=this.el;if(t.__vue__)this.invalid=!0;else{var e=t.nextElementSibling;e&&null!==Y(e,"v-else")&&(nt(e),this.elseEl=e),this.anchor=mt("v-if"),st(t,this.anchor)}},update:function(t){this.invalid||(t?this.frag||this.insert():this.remove())},insert:function(){this.elseFrag&&(this.elseFrag.remove(),this.elseFrag=null),this.factory||(this.factory=new _e(this.vm,this.el)),this.frag=this.factory.create(this._host,this._scope,this._frag),this.frag.before(this.anchor)},remove:function(){this.frag&&(this.frag.remove(),this.frag=null),this.elseEl&&!this.elseFrag&&(this.elseFactory||(this.elseFactory=new _e(this.elseEl._context||this.vm,this.elseEl)),this.elseFrag=this.elseFactory.create(this._host,this._scope,this._frag),this.elseFrag.before(this.anchor))},unbind:function(){this.frag&&this.frag.destroy(),this.elseFrag&&this.elseFrag.destroy()}},ls={bind:function(){var t=this.el.nextElementSibling;t&&null!==Y(t,"v-else")&&(this.elseEl=t)},update:function(t){this.apply(this.el,t),this.elseEl&&this.apply(this.elseEl,!t)},apply:function(t,e){function i(){t.style.display=e?"":"none"}X(t)?G(t,e?1:-1,i,this.vm):i()}},cs={bind:function(){var t=this,e=this.el,i="range"===e.type,n=this.params.lazy,r=this.params.number,s=this.params.debounce,a=!1;if(tn||i||(this.on("compositionstart",function(){a=!0}),this.on("compositionend",function(){a=!1,n||t.listener()})),this.focused=!1,i||n||(this.on("focus",function(){t.focused=!0}),this.on("blur",function(){t.focused=!1,t._frag&&!t._frag.inserted||t.rawListener()})),this.listener=this.rawListener=function(){if(!a&&t._bound){var n=r||i?o(e.value):e.value;t.set(n),ln(function(){t._bound&&!t.focused&&t.update(t._watcher.value)})}},s&&(this.listener=y(this.listener,s)),this.hasjQuery="function"==typeof jQuery,this.hasjQuery){var h=jQuery.fn.on?"on":"bind";jQuery(e)[h]("change",this.rawListener),n||jQuery(e)[h]("input",this.listener)}else this.on("change",this.rawListener),n||this.on("input",this.listener);!n&&Ki&&(this.on("cut",function(){ln(t.listener)}),this.on("keyup",function(e){46!==e.keyCode&&8!==e.keyCode||t.listener()})),(e.hasAttribute("value")||"TEXTAREA"===e.tagName&&e.value.trim())&&(this.afterBind=this.listener)},update:function(t){t=s(t),t!==this.el.value&&(this.el.value=t)},unbind:function(){var t=this.el;if(this.hasjQuery){var e=jQuery.fn.off?"off":"unbind";jQuery(t)[e]("change",this.listener),jQuery(t)[e]("input",this.listener)}}},us={bind:function(){var t=this,e=this.el;this.getValue=function(){if(e.hasOwnProperty("_value"))return e._value;var i=e.value;return t.params.number&&(i=o(i)),i},this.listener=function(){t.set(t.getValue())},this.on("change",this.listener),e.hasAttribute("checked")&&(this.afterBind=this.listener)},update:function(t){this.el.checked=C(t,this.getValue())}},fs={bind:function(){var t=this,e=this,i=this.el;this.forceUpdate=function(){e._watcher&&e.update(e._watcher.get())};var n=this.multiple=i.hasAttribute("multiple");this.listener=function(){var t=$e(i,n);t=e.params.number?qi(t)?t.map(o):o(t):t,e.set(t)},this.on("change",this.listener);var r=$e(i,n,!0);(n&&r.length||!n&&null!==r)&&(this.afterBind=this.listener),this.vm.$on("hook:attached",function(){ln(t.forceUpdate)}),X(i)||ln(this.forceUpdate)},update:function(t){var e=this.el;e.selectedIndex=-1;for(var i,n,r=this.multiple&&qi(t),s=e.options,o=s.length;o--;)i=s[o],n=i.hasOwnProperty("_value")?i._value:i.value,i.selected=r?ke(t,n)>-1:C(t,n)},unbind:function(){this.vm.$off("hook:attached",this.forceUpdate)}},ps={bind:function(){function t(){var t=i.checked;return t&&i.hasOwnProperty("_trueValue")?i._trueValue:!t&&i.hasOwnProperty("_falseValue")?i._falseValue:t}var e=this,i=this.el;this.getValue=function(){return i.hasOwnProperty("_value")?i._value:e.params.number?o(i.value):i.value},this.listener=function(){var n=e._watcher.get();if(qi(n)){var r=e.getValue(),s=b(n,r);i.checked?s<0&&e.set(n.concat(r)):s>-1&&e.set(n.slice(0,s).concat(n.slice(s+1)))}else e.set(t())},this.on("change",this.listener),i.hasAttribute("checked")&&(this.afterBind=this.listener)},update:function(t){var e=this.el;qi(t)?e.checked=b(t,this.getValue())>-1:e.hasOwnProperty("_trueValue")?e.checked=C(t,e._trueValue):e.checked=!!t}},ds={text:cs,radio:us,select:fs,checkbox:ps},vs={priority:Xr,twoWay:!0,handlers:ds,params:["lazy","number","debounce"],bind:function(){this.checkFilters(),this.hasRead&&!this.hasWrite;var t,e=this.el,i=e.tagName;if("INPUT"===i)t=ds[e.type]||ds.text;else if("SELECT"===i)t=ds.select;else{if("TEXTAREA"!==i)return;t=ds.text}e.__v_model=this,t.bind.call(this),this.update=t.update,this._unbind=t.unbind},checkFilters:function(){var t=this.filters;if(t)for(var e=t.length;e--;){var i=jt(this.vm.$options,"filters",t[e].name);("function"==typeof i||i.read)&&(this.hasRead=!0),i.write&&(this.hasWrite=!0)}},unbind:function(){this.el.__v_model=null,this._unbind&&this._unbind()}},ms={esc:27,tab:9,enter:13,space:32,delete:[8,46],up:38,left:37,right:39,down:40},gs={priority:Zr,acceptStatement:!0,keyCodes:ms,bind:function(){if("IFRAME"===this.el.tagName&&"load"!==this.arg){var t=this;this.iframeBind=function(){ot(t.el.contentWindow,t.arg,t.handler,t.modifiers.capture)},this.on("load",this.iframeBind)}},update:function(t){if(this.descriptor.raw||(t=function(){}),"function"==typeof t){this.modifiers.stop&&(t=Ae(t)),this.modifiers.prevent&&(t=Oe(t)),this.modifiers.self&&(t=Te(t));var e=Object.keys(this.modifiers).filter(function(t){return"stop"!==t&&"prevent"!==t&&"self"!==t&&"capture"!==t});e.length&&(t=xe(t,e)),this.reset(),this.handler=t,this.iframeBind?this.iframeBind():ot(this.el,this.arg,this.handler,this.modifiers.capture)}},reset:function(){var t=this.iframeBind?this.el.contentWindow:this.el;this.handler&&at(t,this.arg,this.handler)},unbind:function(){this.reset()}},_s=["-webkit-","-moz-","-ms-"],ys=["Webkit","Moz","ms"],bs=/!important;?$/,ws=Object.create(null),Cs=null,$s={deep:!0,update:function(t){"string"==typeof t?this.el.style.cssText=t:qi(t)?this.handleObject(t.reduce(v,{})):this.handleObject(t||{})},handleObject:function(t){var e,i,n=this.cache||(this.cache={});for(e in n)e in t||(this.handleSingle(e,null),delete n[e]);for(e in t)i=t[e],i!==n[e]&&(n[e]=i,this.handleSingle(e,i))},handleSingle:function(t,e){if(t=Ne(t))if(null!=e&&(e+=""),e){var i=bs.test(e)?"important":"";i?(e=e.replace(bs,"").trim(),this.el.style.setProperty(t.kebab,e,i)):this.el.style[t.camel]=e;
+}else this.el.style[t.camel]=""}},ks="http://www.w3.org/1999/xlink",xs=/^xlink:/,As=/^v-|^:|^@|^(?:is|transition|transition-mode|debounce|track-by|stagger|enter-stagger|leave-stagger)$/,Os=/^(?:value|checked|selected|muted)$/,Ts=/^(?:draggable|contenteditable|spellcheck)$/,Ns={value:"_value","true-value":"_trueValue","false-value":"_falseValue"},js={priority:Yr,bind:function(){var t=this.arg,e=this.el.tagName;t||(this.deep=!0);var i=this.descriptor,n=i.interp;n&&(i.hasOneTime&&(this.expression=B(n,this._scope||this.vm)),(As.test(t)||"name"===t&&("PARTIAL"===e||"SLOT"===e))&&(this.el.removeAttribute(t),this.invalid=!0))},update:function(t){if(!this.invalid){var e=this.arg;this.arg?this.handleSingle(e,t):this.handleObject(t||{})}},handleObject:$s.handleObject,handleSingle:function(t,e){var i=this.el,n=this.descriptor.interp;if(this.modifiers.camel&&(t=l(t)),!n&&Os.test(t)&&t in i){var r="value"===t&&null==e?"":e;i[t]!==r&&(i[t]=r)}var s=Ns[t];if(!n&&s){i[s]=e;var o=i.__v_model;o&&o.listener()}return"value"===t&&"TEXTAREA"===i.tagName?void i.removeAttribute(t):void(Ts.test(t)?i.setAttribute(t,e?"true":"false"):null!=e&&e!==!1?"class"===t?(i.__v_trans&&(e+=" "+i.__v_trans.id+"-transition"),lt(i,e)):xs.test(t)?i.setAttributeNS(ks,t,e===!0?"":e):i.setAttribute(t,e===!0?"":e):i.removeAttribute(t))}},Es={priority:ts,bind:function(){if(this.arg){var t=this.id=l(this.arg),e=(this._scope||this.vm).$els;i(e,t)?e[t]=this.el:Lt(e,t,this.el)}},unbind:function(){var t=(this._scope||this.vm).$els;t[this.id]===this.el&&(t[this.id]=null)}},Ss={bind:function(){}},Fs={bind:function(){var t=this.el;this.vm.$once("pre-hook:compiled",function(){t.removeAttribute("v-cloak")})}},Ds={text:Hr,html:Qr,for:as,if:hs,show:ls,model:vs,on:gs,bind:js,el:Es,ref:Ss,cloak:Fs},Ps={deep:!0,update:function(t){t?"string"==typeof t?this.setClass(t.trim().split(/\s+/)):this.setClass(Ee(t)):this.cleanup()},setClass:function(t){this.cleanup(t);for(var e=0,i=t.length;e<i;e++){var n=t[e];n&&Se(this.el,n,ct)}this.prevKeys=t},cleanup:function(t){var e=this.prevKeys;if(e)for(var i=e.length;i--;){var n=e[i];(!t||t.indexOf(n)<0)&&Se(this.el,n,ut)}}},Rs={priority:es,params:["keep-alive","transition-mode","inline-template"],bind:function(){this.el.__vue__||(this.keepAlive=this.params.keepAlive,this.keepAlive&&(this.cache={}),this.params.inlineTemplate&&(this.inlineTemplate=ft(this.el,!0)),this.pendingComponentCb=this.Component=null,this.pendingRemovals=0,this.pendingRemovalCb=null,this.anchor=mt("v-component"),st(this.el,this.anchor),this.el.removeAttribute("is"),this.el.removeAttribute(":is"),this.descriptor.ref&&this.el.removeAttribute("v-ref:"+u(this.descriptor.ref)),this.literal&&this.setComponent(this.expression))},update:function(t){this.literal||this.setComponent(t)},setComponent:function(t,e){if(this.invalidatePending(),t){var i=this;this.resolveComponent(t,function(){i.mountComponent(e)})}else this.unbuild(!0),this.remove(this.childVM,e),this.childVM=null},resolveComponent:function(t,e){var i=this;this.pendingComponentCb=w(function(n){i.ComponentName=n.options.name||("string"==typeof t?t:null),i.Component=n,e()}),this.vm._resolveComponent(t,this.pendingComponentCb)},mountComponent:function(t){this.unbuild(!0);var e=this,i=this.Component.options.activate,n=this.getCached(),r=this.build();i&&!n?(this.waitingFor=r,Fe(i,r,function(){e.waitingFor===r&&(e.waitingFor=null,e.transition(r,t))})):(n&&r._updateRef(),this.transition(r,t))},invalidatePending:function(){this.pendingComponentCb&&(this.pendingComponentCb.cancel(),this.pendingComponentCb=null)},build:function(t){var e=this.getCached();if(e)return e;if(this.Component){var i={name:this.ComponentName,el:le(this.el),template:this.inlineTemplate,parent:this._host||this.vm,_linkerCachable:!this.inlineTemplate,_ref:this.descriptor.ref,_asComponent:!0,_isRouterView:this._isRouterView,_context:this.vm,_scope:this._scope,_frag:this._frag};t&&v(i,t);var n=new this.Component(i);return this.keepAlive&&(this.cache[this.Component.cid]=n),n}},getCached:function(){return this.keepAlive&&this.cache[this.Component.cid]},unbuild:function(t){this.waitingFor&&(this.keepAlive||this.waitingFor.$destroy(),this.waitingFor=null);var e=this.childVM;return!e||this.keepAlive?void(e&&(e._inactive=!0,e._updateRef(!0))):void e.$destroy(!1,t)},remove:function(t,e){var i=this.keepAlive;if(t){this.pendingRemovals++,this.pendingRemovalCb=e;var n=this;t.$remove(function(){n.pendingRemovals--,i||t._cleanup(),!n.pendingRemovals&&n.pendingRemovalCb&&(n.pendingRemovalCb(),n.pendingRemovalCb=null)})}else e&&e()},transition:function(t,e){var i=this,n=this.childVM;switch(n&&(n._inactive=!0),t._inactive=!1,this.childVM=t,i.params.transitionMode){case"in-out":t.$before(i.anchor,function(){i.remove(n,e)});break;case"out-in":i.remove(n,function(){t.$before(i.anchor,e)});break;default:i.remove(n),t.$before(i.anchor,e)}},unbind:function(){if(this.invalidatePending(),this.unbuild(),this.cache){for(var t in this.cache)this.cache[t].$destroy();this.cache=null}}},Ls=Mn._propBindingModes,Hs={},Is=/^[$_a-zA-Z]+[\w$]*$/,Ms=Mn._propBindingModes,Ws={bind:function(){var t=this.vm,e=t._context,i=this.descriptor.prop,n=i.path,r=i.parentPath,s=i.mode===Ms.TWO_WAY,o=this.parentWatcher=new re(e,r,function(e){He(t,i,e)},{twoWay:s,filters:i.filters,scope:this._scope});if(Le(t,i,o.value),s){var a=this;t.$once("pre-hook:created",function(){a.childWatcher=new re(t,n,function(t){o.set(t)},{sync:!0})})}},unbind:function(){this.parentWatcher.teardown(),this.childWatcher&&this.childWatcher.teardown()}},Vs=[],Bs=!1,zs="transition",Us="animation",Js=nn+"Duration",qs=sn+"Duration",Qs=Gi&&window.requestAnimationFrame,Gs=Qs?function(t){Qs(function(){Qs(t)})}:function(t){setTimeout(t,50)},Zs=Ue.prototype;Zs.enter=function(t,e){this.cancelPending(),this.callHook("beforeEnter"),this.cb=e,ct(this.el,this.enterClass),t(),this.entered=!1,this.callHookWithCb("enter"),this.entered||(this.cancel=this.hooks&&this.hooks.enterCancelled,Be(this.enterNextTick))},Zs.enterNextTick=function(){var t=this;this.justEntered=!0,Gs(function(){t.justEntered=!1});var e=this.enterDone,i=this.getCssTransitionType(this.enterClass);this.pendingJsCb?i===zs&&ut(this.el,this.enterClass):i===zs?(ut(this.el,this.enterClass),this.setupCssCb(rn,e)):i===Us?this.setupCssCb(on,e):e()},Zs.enterDone=function(){this.entered=!0,this.cancel=this.pendingJsCb=null,ut(this.el,this.enterClass),this.callHook("afterEnter"),this.cb&&this.cb()},Zs.leave=function(t,e){this.cancelPending(),this.callHook("beforeLeave"),this.op=t,this.cb=e,ct(this.el,this.leaveClass),this.left=!1,this.callHookWithCb("leave"),this.left||(this.cancel=this.hooks&&this.hooks.leaveCancelled,this.op&&!this.pendingJsCb&&(this.justEntered?this.leaveDone():Be(this.leaveNextTick)))},Zs.leaveNextTick=function(){var t=this.getCssTransitionType(this.leaveClass);if(t){var e=t===zs?rn:on;this.setupCssCb(e,this.leaveDone)}else this.leaveDone()},Zs.leaveDone=function(){this.left=!0,this.cancel=this.pendingJsCb=null,this.op(),ut(this.el,this.leaveClass),this.callHook("afterLeave"),this.cb&&this.cb(),this.op=null},Zs.cancelPending=function(){this.op=this.cb=null;var t=!1;this.pendingCssCb&&(t=!0,at(this.el,this.pendingCssEvent,this.pendingCssCb),this.pendingCssEvent=this.pendingCssCb=null),this.pendingJsCb&&(t=!0,this.pendingJsCb.cancel(),this.pendingJsCb=null),t&&(ut(this.el,this.enterClass),ut(this.el,this.leaveClass)),this.cancel&&(this.cancel.call(this.vm,this.el),this.cancel=null)},Zs.callHook=function(t){this.hooks&&this.hooks[t]&&this.hooks[t].call(this.vm,this.el)},Zs.callHookWithCb=function(t){var e=this.hooks&&this.hooks[t];e&&(e.length>1&&(this.pendingJsCb=w(this[t+"Done"])),e.call(this.vm,this.el,this.pendingJsCb))},Zs.getCssTransitionType=function(t){if(!(!rn||document.hidden||this.hooks&&this.hooks.css===!1||Je(this.el))){var e=this.type||this.typeCache[t];if(e)return e;var i=this.el.style,n=window.getComputedStyle(this.el),r=i[Js]||n[Js];if(r&&"0s"!==r)e=zs;else{var s=i[qs]||n[qs];s&&"0s"!==s&&(e=Us)}return e&&(this.typeCache[t]=e),e}},Zs.setupCssCb=function(t,e){this.pendingCssEvent=t;var i=this,n=this.el,r=this.pendingCssCb=function(s){s.target===n&&(at(n,t,r),i.pendingCssEvent=i.pendingCssCb=null,!i.pendingJsCb&&e&&e())};ot(n,t,r)};var Xs={priority:Kr,update:function(t,e){var i=this.el,n=jt(this.vm.$options,"transitions",t);t=t||"v",e=e||"v",i.__v_trans=new Ue(i,t,n,this.vm),ut(i,e+"-transition"),ct(i,t+"-transition")}},Ys={style:$s,class:Ps,component:Rs,prop:Ws,transition:Xs},Ks=/^v-bind:|^:/,to=/^v-on:|^@/,eo=/^v-([^:]+)(?:$|:(.*)$)/,io=/\.[^\.]+/g,no=/^(v-bind:|:)?transition$/,ro=1e3,so=2e3;ui.terminal=!0;var oo=/[^\w\-:\.]/,ao=Object.freeze({compile:qe,compileAndLinkProps:Ye,compileRoot:Ke,transclude:_i,resolveSlots:Ci}),ho=/^v-on:|^@/;Oi.prototype._bind=function(){var t=this.name,e=this.descriptor;if(("cloak"!==t||this.vm._isCompiled)&&this.el&&this.el.removeAttribute){var i=e.attr||"v-"+t;this.el.removeAttribute(i)}var n=e.def;if("function"==typeof n?this.update=n:v(this,n),this._setupParams(),this.bind&&this.bind(),this._bound=!0,this.literal)this.update&&this.update(e.raw);else if((this.expression||this.modifiers)&&(this.update||this.twoWay)&&!this._checkStatement()){var r=this;this.update?this._update=function(t,e){r._locked||r.update(t,e)}:this._update=Ai;var s=this._preProcess?p(this._preProcess,this):null,o=this._postProcess?p(this._postProcess,this):null,a=this._watcher=new re(this.vm,this.expression,this._update,{filters:this.filters,twoWay:this.twoWay,deep:this.deep,preProcess:s,postProcess:o,scope:this._scope});this.afterBind?this.afterBind():this.update&&this.update(a.value)}},Oi.prototype._setupParams=function(){if(this.params){var t=this.params;this.params=Object.create(null);for(var e,i,n,r=t.length;r--;)e=u(t[r]),n=l(e),i=K(this.el,e),null!=i?this._setupParamWatcher(n,i):(i=Y(this.el,e),null!=i&&(this.params[n]=""===i||i))}},Oi.prototype._setupParamWatcher=function(t,e){var i=this,n=!1,r=(this._scope||this.vm).$watch(e,function(e,r){if(i.params[t]=e,n){var s=i.paramWatchers&&i.paramWatchers[t];s&&s.call(i,e,r)}else n=!0},{immediate:!0,user:!1});(this._paramUnwatchFns||(this._paramUnwatchFns=[])).push(r)},Oi.prototype._checkStatement=function(){var t=this.expression;if(t&&this.acceptStatement&&!Kt(t)){var e=Yt(t).get,i=this._scope||this.vm,n=function(t){i.$event=t,e.call(i,i),i.$event=null};return this.filters&&(n=i._applyFilters(n,null,this.filters)),this.update(n),!0}},Oi.prototype.set=function(t){this.twoWay&&this._withLock(function(){this._watcher.set(t)})},Oi.prototype._withLock=function(t){var e=this;e._locked=!0,t.call(e),ln(function(){e._locked=!1})},Oi.prototype.on=function(t,e,i){ot(this.el,t,e,i),(this._listeners||(this._listeners=[])).push([t,e])},Oi.prototype._teardown=function(){if(this._bound){this._bound=!1,this.unbind&&this.unbind(),this._watcher&&this._watcher.teardown();var t,e=this._listeners;if(e)for(t=e.length;t--;)at(this.el,e[t][0],e[t][1]);var i=this._paramUnwatchFns;if(i)for(t=i.length;t--;)i[t]();this.vm=this.el=this._watcher=this._listeners=null}};var lo=/[^|]\|[^|]/;Ht(Di),ki(Di),xi(Di),Ti(Di),Ni(Di),ji(Di),Ei(Di),Si(Di),Fi(Di);var co={priority:ss,params:["name"],bind:function(){var t=this.params.name||"default",e=this.vm._slotContents&&this.vm._slotContents[t];e&&e.hasChildNodes()?this.compile(e.cloneNode(!0),this.vm._context,this.vm):this.fallback()},compile:function(t,e,i){if(t&&e){if(this.el.hasChildNodes()&&1===t.childNodes.length&&1===t.childNodes[0].nodeType&&t.childNodes[0].hasAttribute("v-if")){var n=document.createElement("template");n.setAttribute("v-else",""),n.innerHTML=this.el.innerHTML,n._context=this.vm,t.appendChild(n)}var r=i?i._scope:this._scope;this.unlink=e.$compile(t,i,r,this._frag)}t?st(this.el,t):nt(this.el)},fallback:function(){this.compile(ft(this.el,!0),this.vm)},unbind:function(){this.unlink&&this.unlink()}},uo={priority:is,params:["name"],paramWatchers:{name:function(t){hs.remove.call(this),t&&this.insert(t)}},bind:function(){this.anchor=mt("v-partial"),st(this.el,this.anchor),this.insert(this.params.name)},insert:function(t){var e=jt(this.vm.$options,"partials",t,!0);e&&(this.factory=new _e(this.vm,e),hs.insert.call(this))},unbind:function(){this.frag&&this.frag.destroy()}},fo={slot:co,partial:uo},po=as._postProcess,vo=/(\d{3})(?=\d)/g,mo={orderBy:Li,filterBy:Ri,limitBy:Pi,json:{read:function(t,e){return"string"==typeof t?t:JSON.stringify(t,null,arguments.length>1?e:2)},write:function(t){try{return JSON.parse(t)}catch(e){return t}}},capitalize:function(t){return t||0===t?(t=t.toString(),t.charAt(0).toUpperCase()+t.slice(1)):""},uppercase:function(t){return t||0===t?t.toString().toUpperCase():""},lowercase:function(t){return t||0===t?t.toString().toLowerCase():""},currency:function(t,e,i){if(t=parseFloat(t),!isFinite(t)||!t&&0!==t)return"";e=null!=e?e:"$",i=null!=i?i:2;var n=Math.abs(t).toFixed(i),r=i?n.slice(0,-1-i):n,s=r.length%3,o=s>0?r.slice(0,s)+(r.length>3?",":""):"",a=i?n.slice(-1-i):"",h=t<0?"-":"";return h+e+o+r.slice(s).replace(vo,"$1,")+a},pluralize:function(t){var e=d(arguments,1),i=e.length;if(i>1){var n=t%10-1;return n in e?e[n]:e[i-1]}return e[0]+(1===t?"":"s")},debounce:function(t,e){if(t)return e||(e=300),y(t,e)}};return Ii(Di),Di.version="1.0.28",setTimeout(function(){Mn.devtools&&Zi&&Zi.emit("init",Di)},0),Di});
+//# sourceMappingURL=vue.min.js.map
\ No newline at end of file
diff --git a/dist/vue.min.js.map b/dist/vue.min.js.map
new file mode 100644
index 00000000000..e30beb1291a
--- /dev/null
+++ b/dist/vue.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["vue.js"],"names":["global","factory","exports","module","define","amd","Vue","this","set","obj","key","val","hasOwn","_isVue","_data","ob","__ob__","convert","dep","notify","vms","i","length","vm","_proxy","_digest","del","_unproxy","hasOwnProperty","call","isLiteral","exp","literalValueRE","test","isReserved","str","c","charCodeAt","_toString","value","toString","toNumber","parsed","Number","isNaN","toBoolean","stripQuotes","a","b","slice","camelize","replace","camelizeRE","toUpper","_","toUpperCase","hyphenate","hyphenateRE","toLowerCase","classify","classifyRE","bind","fn","ctx","l","arguments","apply","toArray","list","start","ret","Array","extend","to","from","keys","Object","isObject","isPlainObject","OBJECT_STRING","def","enumerable","defineProperty","writable","configurable","_debounce","func","wait","timeout","args","context","timestamp","result","later","last","Date","now","setTimeout","indexOf","arr","cancellable","cb","cancelled","cancel","looseEqual","JSON","stringify","isNative","Ctor","Cache","limit","size","head","tail","undefined","_keymap","create","peek","index","next","eof","len","eatSpace","spaceChr","isStringStart","chr","doubleChr","singleChr","isExpStart","expStartChr","isExpEnd","expChrPair","parseString","stringQuote","escapeChr","parseSpecialExp","inExp","startChr","parseExpression","pipeChr","state","startState","filterArgState","filterState","filterNameState","parseFilterList","filters","push","parseFilter","filter","name","trim","parseFilterArguments","arg","processFilterArg","reservedArgRE","dynamic","stripped","parseDirective","s","hit","cache$1","get","dir","expression","put","escapeRegex","regexEscapeRE","compileRegex","open","config","delimiters","close","unsafeOpen","unsafeDelimiters","unsafeClose","tagRE","RegExp","htmlRE","cache","parseText","text","match","html","first","oneTime","tokens","lastIndex","exec","tag","tokensToExp","map","token","formatToken","join","single","$eval","inlineFilters","filterRE","appendWithTransition","el","target","applyTransition","appendChild","beforeWithTransition","before","removeWithTransition","remove","direction","op","transition","__v_trans","hooks","transitionEndEvent","_isCompiled","$parent","action","query","document","querySelector","inDoc","node","doc","ownerDocument","documentElement","parent","parentNode","nodeType","contains","getAttr","_attr","getAttribute","removeAttribute","getBindAttr","hasBindAttr","hasAttribute","insertBefore","after","nextSibling","removeChild","prepend","firstChild","replaceChild","on","event","useCapture","addEventListener","off","removeEventListener","getClass","classname","className","baseVal","setClass","cls","isIE9","namespaceURI","setAttribute","addClass","classList","add","cur","removeClass","tar","extractContent","asFragment","child","rawContent","isTemplate","isFragment","content","hasChildNodes","trimNode","createDocumentFragment","createElement","isTrimmable","lastChild","data","tagName","createAnchor","persist","anchor","debug","createComment","createTextNode","__v_anchor","findRef","hasAttributes","attrs","attributes","refRE","mapNodeRange","end","removeNodeRange","frag","onRemoved","removed","done","nodes","getOuterHTML","outerHTML","container","cloneNode","innerHTML","checkComponentAttr","options","hasAttrs","commonTagRE","reservedTagRE","getIsBinding","resolveAsset","id","is","mergeData","toVal","fromVal","mergeAssets","parentVal","childVal","res","guardArrayAssets","guardComponents","components","ids","guardProps","props","isArray","type","assets","asset","mergeOptions","mergeField","strat","strats","defaultStrat","mixins","mixin","mixinOptions","prototype","warnMissing","camelizedId","charAt","Dep","uid$1","subs","withoutConversion","shouldConvert","Observer","augment","hasProto","protoAugment","copyAugment","arrayMethods","arrayKeys","observeArray","walk","src","__proto__","observe","isExtensible","addVm","defineReactive","property","getOwnPropertyDescriptor","getter","setter","childOb","depend","e","newVal","initMixin","_init","$el","$root","$children","$refs","$els","_watchers","_directives","_uid","uid","_events","_eventsCount","_isFragment","_fragment","_fragmentStart","_fragmentEnd","_isDestroyed","_isReady","_isAttached","_isBeingDestroyed","_vForRemoving","_unlinkFn","_context","_scope","_frag","children","$options","constructor","_updateRef","_callHook","_initState","_initEvents","$mount","getPathCharType","ch","code","formatSubPath","path","trimmed","parse","maybeUnescapeQuote","nextChar","mode","IN_SINGLE_QUOTE","IN_DOUBLE_QUOTE","newChar","actions","APPEND","typeMap","BEFORE_PATH","subPathDepth","PUSH","INC_SUB_PATH_DEPTH","PUSH_SUB_PATH","IN_SUB_PATH","pathStateMachine","ERROR","AFTER_PATH","raw","parsePath","pathCache","getPath","parseExpression$1","setPath","original","$set","noop","save","isString","saved","newlineRE","rewrite","allowedKeywordsRE","restoreRE","restore","compileGetter","improperKeywordsRE","body","saveRE","wsRE","identRE","makeGetterFn","Function","compileSetter","scope","needSet","expressionCache","isSimplePath","pathTestRE","literalValueRE$1","resetBatcherState","queue","userQueue","has","circular","waiting","flushBatcherQueue","_again","runBatcherQueue","devtools","emit","watcher","run","pushWatcher","q","user","nextTick","Watcher","expOrFn","isFn","uid$2","active","dirty","lazy","deps","newDeps","depIds","_Set","newDepIds","prevError","twoWay","queued","shallow","traverse","seen","seenObjects","clear","isA","isO","depId","isRealTemplate","stringToFragment","templateString","cacheKey","templateCache","tagMatch","tagRE$1","entityMatch","entityRE","commentMatch","commentRE","wrap","efault","depth","prefix","suffix","nodeToFragment","textContent","clonedNode","querySelectorAll","cloned","hasBrokenTemplate","tempClone","hasTextareaCloneBug","parseTemplate","template","shouldClone","idSelectorCache","getElementById","Fragment","linker","host","parentFrag","childFrags","inserted","unlink","childNodes","singleBefore","singleRemove","multiBefore","multiRemove","__v_frag","withTransition","method","callHook","attach","shouldCallRemove","self","beforeRemove","detach","destroy","FragmentFactory","cid","cacheId","linkerCache","compile","findPrevFrag","previousSibling","forId","range","n","Math","floor","getTrackByKey","trackByKey","findVmFromFrag","__vue__","getValue","multi","init","selected","_value","indexOf$1","keyFilter","handler","codes","charCode","parseInt","keyCodes","concat","keyCode","stopFilter","stopPropagation","preventFilter","preventDefault","selfFilter","currentTarget","normalize","prop","propCache","camel","upper","testEl","prefixed","prefixes","style","kebab","camelPrefixes","normalize$1","_key","k","split","callActivateHooks","called","total","compileProps","propOptions","attr","propsData","names","empty","identRE$1","propBindingModes","ONE_WAY","TWO_WAY","ONE_TIME","optimizedLiteral","parentPath","makePropsLinkFn","_props","inlineProps","initProp","$get","_bindDir","propDef","Boolean","processPropValue","rawValue","isSimple","getPropDefaultValue","coerceProp","coerced","assertProp","updateProp","required","valid","expectedTypes","assertedType","assertType","expectedType","validator","coerce","String","pushJob","job","queue$1","flush","f","offsetHeight","Transition","enterClass","leaveClass","pendingCssEvent","pendingCssCb","pendingJsCb","justEntered","entered","left","typeCache","forEach","m","isHidden","rect","getBoundingClientRect","width","height","offsetWidth","getClientRects","partial","nodeLinkFn","_asComponent","compileNode","childLinkFn","terminal","isScript","compileNodeList","dirs","linkAndCapture","makeUnlinkFn","originalDirCount","sortDirectives","_bind","j","groupedMap","priorities","priority","descriptor","DEFAULT_PRIORITY","array","sort","group","contextDirs","destroying","teardownDirs","_teardown","compileAndLinkProps","propsLinkFn","propDirs","compileRoot","contextOptions","contextLinkFn","replacerLinkFn","containerAttrs","_containerAttrs","replacerAttrs","_replacerAttrs","compileDirectives","selfDirs","compileTextNode","compileElement","skip","linkFn","checkTerminalDirectives","checkElementDirectives","checkComponent","_skip","removeText","wholeText","processTextToken","makeTextNodeLinkFn","setTokenType","directives","fragClone","nodeList","linkFns","makeChildLinkFn","childrenLinkFn","makeTerminalNodeLinkFn","component","ref","internalDirectives","modifiers","literal","componentLinkFn","prev","previousElementSibling","matched","dirName","rawName","termDef","modifierRE","dirAttrRE","DEFAULT_TERMINAL_PRIORITY","parseModifiers","pushDir","interpTokens","hasOneTimeToken","hasOneTime","interp","dirDef","transitionRE","bindRE","onRE","makeNodeLinkFn","transclude","extractAttrs","_content","transcludeTemplate","replacer","mergeAttrs","specialCharRE","resolveSlots","contents","_slotContents","extractFragment","stateMixin","makeComputedGetter","owner","evaluate","newData","_setData","_initProps","_initMeta","_initMethods","_initData","_initComputed","_propsUnlinkFn","dataFn","oldData","removeVm","update","computed","userDef","methods","metas","_meta","eventsMixin","registerComponentEvents","eventRE","_fromParent","$on","registerCallbacks","hash","handlers","register","onAttached","callAttach","onDetached","callDetach","events","watch","_initDOMHooks","hook","$emit","noop$1","Directive","_locked","_bound","_listeners","_host","lifecycleMixin","_ref","refs","_compile","_initElement","rootLinker","contentLinkFn","ctor","_linkerCachable","rootUnlinkFn","contentUnlinkFn","_destroy","deferCleanup","_cleanup","destroyReady","pendingRemoval","cleanupIfPossible","$remove","$destroy","teardown","$off","miscMixin","_applyFilters","oldValue","write","offset","read","_resolveComponent","resolved","requested","pendingCallbacks","cbs","reason","dataAPI","clean","asStatement","$arguments","$delete","$watch","deep","sync","immediate","filterRE$1","$interpolate","$log","console","log","domAPI","insert","op1","op2","targetIsDetached","shouldCallHook","append","beforeWithCb","removeWithCb","$nextTick","$appendTo","$prependTo","$before","$after","inDocument","realCb","eventsAPI","modifyListenerCount","count","hookRE","$once","splice","isSource","shouldPropagate","hasParentCbs","some","$broadcast","source","$dispatch","lifecycleAPI","ready","$compile","limitBy","filterBy","search","delimiter","convertArray","item","$value","$key","orderBy","baseCompare","sortKeyIndex","sortKey","sortKeys","order","comparator","firstArg","installGlobalAPI","createClass","elementDirectives","transitions","partials","util","compiler","parsers","directive","extendOptions","Super","isFirstExtend","_Ctor","Sub","_assetTypes","use","plugin","installed","unshift","install","definition","inBrowser","window","__VUE_DEVTOOLS_GLOBAL_HOOK__","UA","navigator","userAgent","isIE","isAndroid","isIOS","transitionProp","animationProp","animationEndEvent","isWebkitTrans","ontransitionend","onwebkittransitionend","isWebkitAnim","onanimationend","onwebkitanimationend","nextTickHandler","pending","copies","callbacks","timerFunc","Promise","p","resolve","then","MutationObserver","counter","observer","textNode","characterData","Set","entry","shift","newer","older","returnEntry","91","123","40","freeze","defineProperties","silent","async","warnExpressionErrors","_delimitersChanged","_propBindingModes","_maxUpdateCount","warn","optionMergeStrategies","instanceData","defaultData","created","attached","detached","beforeCompile","compiled","beforeDestroy","destroyed","activate","addSub","sub","removeSub","addDep","arrayProto","getOwnPropertyNames","items","debounce","IN_PATH","BEFORE_IDENT","IN_IDENT","ws","ident","[",".","0","number","'","\"","]","else","allowedKeywords","improperKeywords","beforeGet","preProcess","postProcess","afterGet","forContext","$forContext","alias","_withLock","$index","tmp","current","text$1","legend","tr","col","td","th","option","optgroup","thead","tbody","colgroup","caption","tfoot","g","defs","symbol","image","circle","ellipse","line","polygon","polyline","t","placeholder","swap","_watcher","ON","MODEL","BIND","TRANSITION","EL","COMPONENT","PARTIAL","IF","FOR","SLOT","uid$3","vFor","params","inMatch","itMatch","iterator","isOption","diff","updateRef","updateModel","primitive","convertedFromObject","fromObject","trackBy","oldFrags","frags","getCachedFrag","reused","fresh","removalIndex","totalRemoved","deleteCachedFrag","w","targetPrev","prevEl","currentPrev","insertionIndex","staggerCb","staggerAnchor","move","parentScope","cacheFrag","model","__v_model","forceUpdate","staggerAmount","getStagger","trans","stagger","_preProcess","_postProcess","unbind","vIf","invalid","nextElementSibling","elseEl","elseFrag","elseFactory","show","toggle","display","text$2","isRange","composing","listener","focused","rawListener","hasjQuery","jQuery","afterBind","radio","checked","select","_this","multiple","initValue","selectedIndex","checkbox","getBooleanValue","_trueValue","_falseValue","checkFilters","hasRead","hasWrite","_unbind","esc","tab","enter","space","delete","up","right","down","on$1","acceptStatement","iframeBind","contentWindow","capture","stop","prevent","reset","importantRE","cssText","handleObject","reduce","handleSingle","isImportant","setProperty","xlinkNS","xlinkRE","disallowedInterpAttrRE","attrWithPropsRE","enumeratedAttrRE","modelProps","true-value","false-value","bind$1","attrValue","modelProp","setAttributeNS","cloak","for","if","vClass","cleanup","prevKeys","keepAlive","inlineTemplate","pendingComponentCb","Component","pendingRemovals","pendingRemovalCb","setComponent","invalidatePending","resolveComponent","mountComponent","unbuild","childVM","ComponentName","activateHooks","cached","getCached","newComponent","build","waitingFor","extraOptions","_isRouterView","defer","_inactive","transitionMode","bindingModes","childKey","parentKey","parentWatcher","childWatcher","TYPE_TRANSITION","TYPE_ANIMATION","transDurationProp","animDurationProp","raf","requestAnimationFrame","waitForTransitionStart","p$1","cancelPending","callHookWithCb","enterCancelled","enterNextTick","enterDone","getCssTransitionType","setupCssCb","leave","leaveCancelled","leaveDone","leaveNextTick","hasPending","hidden","css","inlineStyles","computedStyles","getComputedStyle","transDuration","animDuration","onEnd","transition$1","oldId","class","_setupParams","_checkStatement","_update","oldVal","mappedKey","_setupParamWatcher","unwatch","paramWatchers","_paramUnwatchFns","$event","listeners","unwatchFns","slot","fallback","elseBlock","digitsRE","json","indent","capitalize","uppercase","lowercase","currency","_currency","decimals","parseFloat","isFinite","stringified","abs","toFixed","_int","_float","sign","pluralize","delay","version"],"mappings":";;;;;CAKC,SAAUA,EAAQC,GACE,gBAAZC,UAA0C,mBAAXC,QAAyBA,OAAOD,QAAUD,IAC9D,kBAAXG,SAAyBA,OAAOC,IAAMD,OAAOH,GACnDD,EAAOM,IAAML,KACdM,KAAM,WAAe,YAEvB,SAASC,GAAIC,EAAKC,EAAKC,GACrB,GAAIC,EAAOH,EAAKC,GAEd,YADAD,EAAIC,GAAOC,EAGb,IAAIF,EAAII,OAEN,WADAL,GAAIC,EAAIK,MAAOJ,EAAKC,EAGtB,IAAII,GAAKN,EAAIO,MACb,KAAKD,EAEH,YADAN,EAAIC,GAAOC,EAKb,IAFAI,EAAGE,QAAQP,EAAKC,GAChBI,EAAGG,IAAIC,SACHJ,EAAGK,IAEL,IADA,GAAIC,GAAIN,EAAGK,IAAIE,OACRD,KAAK,CACV,GAAIE,GAAKR,EAAGK,IAAIC,EAChBE,GAAGC,OAAOd,GACVa,EAAGE,UAGP,MAAOd,GAUT,QAASe,GAAIjB,EAAKC,GAChB,GAAKE,EAAOH,EAAKC,GAAjB,OAGOD,GAAIC,EACX,IAAIK,GAAKN,EAAIO,MACb,KAAKD,EAKH,YAJIN,EAAII,eACCJ,GAAIK,MAAMJ,GACjBD,EAAIgB,WAKR,IADAV,EAAGG,IAAIC,SACHJ,EAAGK,IAEL,IADA,GAAIC,GAAIN,EAAGK,IAAIE,OACRD,KAAK,CACV,GAAIE,GAAKR,EAAGK,IAAIC,EAChBE,GAAGI,SAASjB,GACZa,EAAGE,YAcT,QAASb,GAAOH,EAAKC,GACnB,MAAOkB,IAAeC,KAAKpB,EAAKC,GAYlC,QAASoB,GAAUC,GACjB,MAAOC,IAAeC,KAAKF,GAU7B,QAASG,GAAWC,GAClB,GAAIC,IAAKD,EAAM,IAAIE,WAAW,EAC9B,OAAa,MAAND,GAAoB,KAANA,EAWvB,QAASE,GAAUC,GACjB,MAAgB,OAATA,EAAgB,GAAKA,EAAMC,WAWpC,QAASC,GAASF,GAChB,GAAqB,gBAAVA,GACT,MAAOA,EAEP,IAAIG,GAASC,OAAOJ,EACpB,OAAOK,OAAMF,GAAUH,EAAQG,EAWnC,QAASG,GAAUN,GACjB,MAAiB,SAAVA,GAAoC,UAAVA,GAA4BA,EAU/D,QAASO,GAAYX,GACnB,GAAIY,GAAIZ,EAAIE,WAAW,GACnBW,EAAIb,EAAIE,WAAWF,EAAIb,OAAS,EACpC,OAAOyB,KAAMC,GAAY,KAAND,GAAoB,KAANA,EAAiCZ,EAAnBA,EAAIc,MAAM,GAAG,GAY9D,QAASC,GAASf,GAChB,MAAOA,GAAIgB,QAAQC,GAAYC,GAGjC,QAASA,GAAQC,EAAGlB,GAClB,MAAOA,GAAIA,EAAEmB,cAAgB,GAY/B,QAASC,GAAUrB,GACjB,MAAOA,GAAIgB,QAAQM,GAAa,SAASN,QAAQM,GAAa,SAASC,cAiBzE,QAASC,GAASxB,GAChB,MAAOA,GAAIgB,QAAQS,GAAYP,GAWjC,QAASQ,GAAKC,EAAIC,GAChB,MAAO,UAAUhB,GACf,GAAIiB,GAAIC,UAAU3C,MAClB,OAAO0C,GAAIA,EAAI,EAAIF,EAAGI,MAAMH,EAAKE,WAAaH,EAAGjC,KAAKkC,EAAKhB,GAAKe,EAAGjC,KAAKkC,IAY5E,QAASI,GAAQC,EAAMC,GACrBA,EAAQA,GAAS,CAGjB,KAFA,GAAIhD,GAAI+C,EAAK9C,OAAS+C,EAClBC,EAAM,GAAIC,OAAMlD,GACbA,KACLiD,EAAIjD,GAAK+C,EAAK/C,EAAIgD,EAEpB,OAAOC,GAUT,QAASE,GAAOC,EAAIC,GAGlB,IAFA,GAAIC,GAAOC,OAAOD,KAAKD,GACnBrD,EAAIsD,EAAKrD,OACND,KACLoD,EAAGE,EAAKtD,IAAMqD,EAAKC,EAAKtD,GAE1B,OAAOoD,GAYT,QAASI,GAASpE,GAChB,MAAe,QAARA,GAA+B,gBAARA,GAchC,QAASqE,GAAcrE,GACrB,MAAO+B,IAASX,KAAKpB,KAASsE,GAqBhC,QAASC,GAAIvE,EAAKC,EAAKC,EAAKsE,GAC1BL,OAAOM,eAAezE,EAAKC,GACzB6B,MAAO5B,EACPsE,aAAcA,EACdE,UAAU,EACVC,cAAc,IAalB,QAASC,GAAUC,EAAMC,GACvB,GAAIC,GAASC,EAAMC,EAASC,EAAWC,EACnCC,EAAQ,QAASA,KACnB,GAAIC,GAAOC,KAAKC,MAAQL,CACpBG,GAAOP,GAAQO,GAAQ,EACzBN,EAAUS,WAAWJ,EAAON,EAAOO,IAEnCN,EAAU,KACVI,EAASN,EAAKpB,MAAMwB,EAASD,GACxBD,IAASE,EAAUD,EAAO,OAGnC,OAAO,YAOL,MANAC,GAAUnF,KACVkF,EAAOxB,UACP0B,EAAYI,KAAKC,MACZR,IACHA,EAAUS,WAAWJ,EAAON,IAEvBK,GAYX,QAASM,GAAQC,EAAK1F,GAEpB,IADA,GAAIY,GAAI8E,EAAI7E,OACLD,KACL,GAAI8E,EAAI9E,KAAOZ,EAAK,MAAOY,EAE7B,QAAO,EAUT,QAAS+E,GAAYtC,GACnB,GAAIuC,GAAK,QAASA,KAChB,IAAKA,EAAGC,UACN,MAAOxC,GAAGI,MAAM3D,KAAM0D,WAM1B,OAHAoC,GAAGE,OAAS,WACVF,EAAGC,WAAY,GAEVD,EAYT,QAASG,GAAWzD,EAAGC,GAErB,MAAOD,IAAKC,MAAM6B,EAAS9B,KAAM8B,EAAS7B,KAAKyD,KAAKC,UAAU3D,KAAO0D,KAAKC,UAAU1D,GAmCtF,QAAS2D,GAASC,GAChB,MAAQ,cAAc3E,KAAK2E,EAAKpE,YAkGlC,QAASqE,GAAMC,GACbvG,KAAKwG,KAAO,EACZxG,KAAKuG,MAAQA,EACbvG,KAAKyG,KAAOzG,KAAK0G,KAAOC,OACxB3G,KAAK4G,QAAUvC,OAAOwC,OAAO,MA4H/B,QAASC,KACP,MAAOlF,IAAIE,WAAWiF,GAAQ,GAGhC,QAASC,KACP,MAAOpF,IAAIE,aAAaiF,IAG1B,QAASE,KACP,MAAOF,KAASG,GAGlB,QAASC,KACP,KAAOL,MAAWM,IAChBJ,IAIJ,QAASK,GAAcC,GACrB,MAAOA,KAAQC,IAAaD,IAAQE,GAGtC,QAASC,GAAWH,GAClB,MAAOI,IAAYJ,GAGrB,QAASK,GAAS7D,EAAOwD,GACvB,MAAOM,IAAW9D,KAAWwD,EAG/B,QAASO,KAGP,IAFA,GACIP,GADAQ,EAAcd,KAEVC,KAGN,GAFAK,EAAMN,IAEFM,IAAQS,GACVf,QACK,IAAIM,IAAQQ,EACjB,MAKN,QAASE,GAAgBV,GAIvB,IAHA,GAAIW,GAAQ,EACRC,EAAWZ,GAEPL,KAEN,GADAK,EAAMR,IACFO,EAAcC,GAChBO,QAaF,IATIK,IAAaZ,GACfW,IAEEN,EAASO,EAAUZ,IACrBW,IAGFjB,IAEc,IAAViB,EACF,MAUN,QAASE,KAEP,IADA,GAAIrE,GAAQiD,IACJE,KAEN,GADAK,GAAMR,IACFO,EAAcC,IAChBO,QACK,IAAIJ,EAAWH,IACpBU,EAAgBV,QACX,IAAIA,KAAQc,GAAS,CAG1B,GAFApB,IACAM,GAAMR,IACFQ,KAAQc,GAEL,CACDC,KAAUC,IAAcD,KAAUE,KACpCF,GAAQG,GAEV,OALAxB,QAOG,CAAA,GAAIM,KAAQF,KAAaiB,KAAUI,IAAmBJ,KAAUE,IAAiB,CACtFpB,GACA,OAEIkB,KAAUG,KACZH,GAAQI,IAEVzB,IAIJ,MAAOpF,IAAIc,MAAMoB,EAAQ,EAAGiD,KAAU,KAGxC,QAAS2B,KAEP,IADA,GAAIC,OACI1B,KACN0B,EAAQC,KAAKC,IAEf,OAAOF,GAGT,QAASE,KACP,GACI3D,GADA4D,IAYJ,OATAT,IAAQG,GACRM,EAAOC,KAAOZ,IAAkBa,OAEhCX,GAAQE,GACRrD,EAAO+D,IAEH/D,EAAKnE,SACP+H,EAAO5D,KAAOA,GAET4D,EAGT,QAASG,KAEP,IADA,GAAI/D,OACI+B,KAASoB,KAAUG,IAAa,CACtC,GAAIU,GAAMf,GACV,KAAKe,EACH,KAEFhE,GAAK0D,KAAKO,EAAiBD,IAG7B,MAAOhE,GAUT,QAASiE,GAAiBD,GACxB,GAAIE,GAAc1H,KAAKwH,GACrB,OACElH,MAAOE,EAASgH,GAChBG,SAAS,EAGX,IAAIC,GAAW/G,EAAY2G,GACvBG,EAAUC,IAAaJ,CAC3B,QACElH,MAAOqH,EAAUH,EAAMI,EACvBD,QAASA,GAuBf,QAASE,GAAeC,GACtB,GAAIC,GAAMC,GAAQC,IAAIH,EACtB,IAAIC,EACF,MAAOA,EAIT7H,IAAM4H,EACNI,MACA1C,GAAMtF,GAAIb,OACVgG,IAAQ,EACRO,GAAM,GACNe,GAAQC,EAER,IAAIK,EAaJ,OAXI/G,IAAI+D,QAAQ,KAAO,EACrBiE,GAAIC,WAAajI,GAAIoH,QAErBY,GAAIC,WAAa1B,IAAkBa,OACnCL,EAAUD,IACNC,EAAQ5H,SACV6I,GAAIjB,QAAUA,IAIlBe,GAAQI,IAAIN,EAAGI,IACRA,GAkBT,QAASG,GAAYnI,GACnB,MAAOA,GAAIgB,QAAQoH,GAAe,QAGpC,QAASC,KACP,GAAIC,GAAOH,EAAYI,GAAOC,WAAW,IACrCC,EAAQN,EAAYI,GAAOC,WAAW,IACtCE,EAAaP,EAAYI,GAAOI,iBAAiB,IACjDC,EAAcT,EAAYI,GAAOI,iBAAiB,GACtDE,IAAQ,GAAIC,QAAOJ,EAAa,gBAAkBE,EAAc,IAAMN,EAAO,gBAAkBG,EAAO,KACtGM,GAAS,GAAID,QAAO,IAAMJ,EAAa,gBAAkBE,EAAc,KAEvEI,GAAQ,GAAItE,GAAM,KAcpB,QAASuE,GAAUC,GACZF,IACHX,GAEF,IAAIR,GAAMmB,GAAMjB,IAAImB,EACpB,IAAIrB,EACF,MAAOA,EAET,KAAKgB,GAAM/I,KAAKoJ,GACd,MAAO,KAMT,KAJA,GAEIC,GAAOhE,EAAOiE,EAAMhJ,EAAOiJ,EAAOC,EAFlCC,KACAC,EAAYX,GAAMW,UAAY,EAG3BL,EAAQN,GAAMY,KAAKP,IAExB/D,EAAQgE,EAAMhE,MAEVA,EAAQqE,GACVD,EAAOvC,MACL5G,MAAO8I,EAAKpI,MAAM0I,EAAWrE,KAIjCiE,EAAOL,GAAOjJ,KAAKqJ,EAAM,IACzB/I,EAAQgJ,EAAOD,EAAM,GAAKA,EAAM,GAChCE,EAAQjJ,EAAMF,WAAW,GACzBoJ,EAAoB,KAAVD,EACVjJ,EAAQkJ,EAAUlJ,EAAMU,MAAM,GAAKV,EACnCmJ,EAAOvC,MACL0C,KAAK,EACLtJ,MAAOA,EAAMgH,OACbgC,KAAMA,EACNE,QAASA,IAEXE,EAAYrE,EAAQgE,EAAM,GAAGhK,MAQ/B,OANIqK,GAAYN,EAAK/J,QACnBoK,EAAOvC,MACL5G,MAAO8I,EAAKpI,MAAM0I,KAGtBR,GAAMd,IAAIgB,EAAMK,GACTA,EAaT,QAASI,GAAYJ,EAAQnK,GAC3B,MAAImK,GAAOpK,OAAS,EACXoK,EAAOK,IAAI,SAAUC,GAC1B,MAAOC,GAAYD,EAAOzK,KACzB2K,KAAK,KAEDD,EAAYP,EAAO,GAAInK,GAAI,GAatC,QAAS0K,GAAYD,EAAOzK,EAAI4K,GAC9B,MAAOH,GAAMH,IAAMG,EAAMP,SAAWlK,EAAK,IAAMA,EAAG6K,MAAMJ,EAAMzJ,OAAS,IAAM8J,EAAcL,EAAMzJ,MAAO4J,GAAU,IAAMH,EAAMzJ,MAAQ,IAiBxI,QAAS8J,GAActK,EAAKoK,GAC1B,GAAKG,GAASrK,KAAKF,GAEZ,CACL,GAAIoI,GAAML,EAAe/H,EACzB,OAAKoI,GAAIjB,QAGA,sBAAwBiB,EAAIC,WACnC,SACA3D,KAAKC,UAAUyD,EAAIjB,SACnB,UALO,IAAMnH,EAAM,IAJrB,MAAOoK,GAASpK,EAAM,IAAMA,EAAM,IAyItC,QAASwK,GAAqBC,EAAIC,EAAQlL,EAAI8E,GAC5CqG,EAAgBF,EAAI,EAAG,WACrBC,EAAOE,YAAYH,IAClBjL,EAAI8E,GAYT,QAASuG,GAAqBJ,EAAIC,EAAQlL,EAAI8E,GAC5CqG,EAAgBF,EAAI,EAAG,WACrBK,GAAOL,EAAIC,IACVlL,EAAI8E,GAWT,QAASyG,GAAqBN,EAAIjL,EAAI8E,GACpCqG,EAAgBF,GAAI,EAAI,WACtBO,GAAOP,IACNjL,EAAI8E,GAeT,QAASqG,GAAgBF,EAAIQ,EAAWC,EAAI1L,EAAI8E,GAC9C,GAAI6G,GAAaV,EAAGW,SACpB,KAAKD,IAGJA,EAAWE,QAAUC,KAErB9L,EAAG+L,aAIJ/L,EAAGgM,UAAYhM,EAAGgM,QAAQD,YAGxB,MAFAL,UACI5G,GAAIA,IAGV,IAAImH,GAASR,EAAY,EAAI,QAAU,OACvCE,GAAWM,GAAQP,EAAI5G,GAiBzB,QAASoH,GAAMjB,GACb,GAAkB,gBAAPA,GAAiB,CAE1BA,EAAKkB,SAASC,cAAcnB,GAK9B,MAAOA,GAeT,QAASoB,GAAMC,GACb,IAAKA,EAAM,OAAO,CAClB,IAAIC,GAAMD,EAAKE,cAAcC,gBACzBC,EAASJ,EAAKK,UAClB,OAAOJ,KAAQD,GAAQC,IAAQG,MAAaA,GAA8B,IAApBA,EAAOE,WAAkBL,EAAIM,SAASH,IAU9F,QAASI,GAAQR,EAAMS,GACrB,GAAI3N,GAAMkN,EAAKU,aAAaD,EAI5B,OAHY,QAAR3N,GACFkN,EAAKW,gBAAgBF,GAEhB3N,EAWT,QAAS8N,GAAYZ,EAAMvE,GACzB,GAAI3I,GAAM0N,EAAQR,EAAM,IAAMvE,EAI9B,OAHY,QAAR3I,IACFA,EAAM0N,EAAQR,EAAM,UAAYvE,IAE3B3I,EAWT,QAAS+N,IAAYb,EAAMvE,GACzB,MAAOuE,GAAKc,aAAarF,IAASuE,EAAKc,aAAa,IAAMrF,IAASuE,EAAKc,aAAa,UAAYrF,GAUnG,QAASuD,IAAOL,EAAIC,GAClBA,EAAOyB,WAAWU,aAAapC,EAAIC,GAUrC,QAASoC,IAAMrC,EAAIC,GACbA,EAAOqC,YACTjC,GAAOL,EAAIC,EAAOqC,aAElBrC,EAAOyB,WAAWvB,YAAYH,GAUlC,QAASO,IAAOP,GACdA,EAAG0B,WAAWa,YAAYvC,GAU5B,QAASwC,IAAQxC,EAAIC,GACfA,EAAOwC,WACTpC,GAAOL,EAAIC,EAAOwC,YAElBxC,EAAOE,YAAYH,GAWvB,QAASrJ,IAAQsJ,EAAQD,GACvB,GAAIyB,GAASxB,EAAOyB,UAChBD,IACFA,EAAOiB,aAAa1C,EAAIC,GAa5B,QAAS0C,IAAG3C,EAAI4C,EAAO/I,EAAIgJ,GACzB7C,EAAG8C,iBAAiBF,EAAO/I,EAAIgJ,GAWjC,QAASE,IAAI/C,EAAI4C,EAAO/I,GACtBmG,EAAGgD,oBAAoBJ,EAAO/I,GAWhC,QAASoJ,IAASjD,GAChB,GAAIkD,GAAYlD,EAAGmD,SAInB,OAHyB,gBAAdD,KACTA,EAAYA,EAAUE,SAAW,IAE5BF,EAaT,QAASG,IAASrD,EAAIsD,GAEhBC,KAAU,OAAO9N,KAAKuK,EAAGwD,cAC3BxD,EAAGmD,UAAYG,EAEftD,EAAGyD,aAAa,QAASH,GAW7B,QAASI,IAAS1D,EAAIsD,GACpB,GAAItD,EAAG2D,UACL3D,EAAG2D,UAAUC,IAAIN,OACZ,CACL,GAAIO,GAAM,IAAMZ,GAASjD,GAAM,GAC3B6D,GAAInK,QAAQ,IAAM4J,EAAM,KAAO,GACjCD,GAASrD,GAAK6D,EAAMP,GAAKvG,SAY/B,QAAS+G,IAAY9D,EAAIsD,GACvB,GAAItD,EAAG2D,UACL3D,EAAG2D,UAAUpD,OAAO+C,OACf,CAGL,IAFA,GAAIO,GAAM,IAAMZ,GAASjD,GAAM,IAC3B+D,EAAM,IAAMT,EAAM,IACfO,EAAInK,QAAQqK,IAAQ,GACzBF,EAAMA,EAAIlN,QAAQoN,EAAK,IAEzBV,IAASrD,EAAI6D,EAAI9G,QAEdiD,EAAGmD,WACNnD,EAAGgC,gBAAgB,SAavB,QAASgC,IAAehE,EAAIiE,GAC1B,GAAIC,GACAC,CAKJ,IAHIC,GAAWpE,IAAOqE,GAAWrE,EAAGsE,WAClCtE,EAAKA,EAAGsE,SAENtE,EAAGuE,gBAIL,IAHAC,GAASxE,GACTmE,EAAaF,EAAa/C,SAASuD,yBAA2BvD,SAASwD,cAAc,OAE9ER,EAAQlE,EAAGyC,YAEhB0B,EAAWhE,YAAY+D,EAG3B,OAAOC,GAUT,QAASK,IAASnD,GAGhB,IAFA,GAAI6C,GAEIA,EAAQ7C,EAAKoB,WAAYkC,GAAYT,IAC3C7C,EAAKkB,YAAY2B,EAEnB,MAAQA,EAAQ7C,EAAKuD,UAAWD,GAAYT,IAC1C7C,EAAKkB,YAAY2B,GAKrB,QAASS,IAAYtD,GACnB,MAAOA,KAA2B,IAAlBA,EAAKM,WAAmBN,EAAKwD,KAAK9H,QAA4B,IAAlBsE,EAAKM,UAWnE,QAASyC,IAAWpE,GAClB,MAAOA,GAAG8E,SAAwC,aAA7B9E,EAAG8E,QAAQ5N,cAqBlC,QAAS6N,IAAaT,EAASU,GAC7B,GAAIC,GAAS/G,GAAOgH,MAAQhE,SAASiE,cAAcb,GAAWpD,SAASkE,eAAeJ,EAAU,IAAM,GAEtG,OADAC,GAAOI,YAAa,EACbJ,EAYT,QAASK,IAAQjE,GACf,GAAIA,EAAKkE,gBAEP,IAAK,GADDC,GAAQnE,EAAKoE,WACR5Q,EAAI,EAAG2C,EAAIgO,EAAM1Q,OAAQD,EAAI2C,EAAG3C,IAAK,CAC5C,GAAIiI,GAAO0I,EAAM3Q,GAAGiI,IACpB,IAAI4I,GAAMjQ,KAAKqH,GACb,MAAOpG,GAASoG,EAAKnG,QAAQ+O,GAAO,MAc5C,QAASC,IAAatE,EAAMuE,EAAKnF,GAE/B,IADA,GAAI1F,GACGsG,IAASuE,GACd7K,EAAOsG,EAAKiB,YACZ7B,EAAGY,GACHA,EAAOtG,CAET0F,GAAGmF,GAeL,QAASC,IAAgBhO,EAAO+N,EAAK7Q,EAAI+Q,EAAMjM,GAS7C,QAASkM,KAEP,GADAC,IACIC,GAAQD,GAAWE,EAAMpR,OAAQ,CACnC,IAAK,GAAID,GAAI,EAAGA,EAAIqR,EAAMpR,OAAQD,IAChCiR,EAAK3F,YAAY+F,EAAMrR,GAEzBgF,IAAMA,KAdV,GAAIoM,IAAO,EACPD,EAAU,EACVE,IACJP,IAAa9N,EAAO+N,EAAK,SAAUvE,GAC7BA,IAASuE,IAAKK,GAAO,GACzBC,EAAMvJ,KAAK0E,GACXf,EAAqBe,EAAMtM,EAAIgR,KAoBnC,QAAS1B,IAAWhD,GAClB,MAAOA,IAA0B,KAAlBA,EAAKM,SAWtB,QAASwE,IAAanG,GACpB,GAAIA,EAAGoG,UACL,MAAOpG,GAAGoG,SAEV,IAAIC,GAAYnF,SAASwD,cAAc,MAEvC,OADA2B,GAAUlG,YAAYH,EAAGsG,WAAU,IAC5BD,EAAUE,UAgBrB,QAASC,IAAmBxG,EAAIyG,GAC9B,GAAIpH,GAAMW,EAAG8E,QAAQ5N,cACjBwP,EAAW1G,EAAGuF,eAClB,IAAKoB,GAAYlR,KAAK4J,IAASuH,GAAcnR,KAAK4J,IAS3C,GAAIqH,EACT,MAAOG,IAAa7G,EAAIyG,OAV8B,CACtD,GAAIK,GAAaL,EAAS,aAAcpH,GACtC,OAAS0H,GAAI1H,EAEb,IAAI2H,GAAKN,GAAYG,GAAa7G,EAAIyG,EACtC,IAAIO,EACF,MAAOA,IAgBf,QAASH,IAAa7G,EAAIyG,GAExB,GAAIlR,GAAMyK,EAAG+B,aAAa,KAC1B,IAAW,MAAPxM,GACF,GAAIuR,GAAaL,EAAS,aAAclR,GAEtC,MADAyK,GAAGgC,gBAAgB,OACV+E,GAAIxR,OAIf,IADAA,EAAM0M,EAAYjC,EAAI,MACX,MAAPzK,EACF,OAASwR,GAAIxR,EAAK6H,SAAS,GAuBjC,QAAS6J,IAAUhP,EAAIC,GACrB,GAAIhE,GAAKgT,EAAOC,CAChB,KAAKjT,IAAOgE,GACVgP,EAAQjP,EAAG/D,GACXiT,EAAUjP,EAAKhE,GACVE,EAAO6D,EAAI/D,GAELmE,EAAS6O,IAAU7O,EAAS8O,IACrCF,GAAUC,EAAOC,GAFjBnT,EAAIiE,EAAI/D,EAAKiT,EAKjB,OAAOlP,GAwET,QAASmP,IAAYC,EAAWC,GAC9B,GAAIC,GAAMnP,OAAOwC,OAAOyM,GAAa,KACrC,OAAOC,GAAWtP,EAAOuP,EAAKC,GAAiBF,IAAaC,EA0D9D,QAASE,IAAgBhB,GACvB,GAAIA,EAAQiB,WAKV,IAAK,GAFDlP,GAFAkP,EAAajB,EAAQiB,WAAaF,GAAiBf,EAAQiB,YAC3DC,EAAMvP,OAAOD,KAAKuP,GAGb7S,EAAI,EAAG2C,EAAImQ,EAAI7S,OAAQD,EAAI2C,EAAG3C,IAAK,CAC1C,GAAIX,GAAMyT,EAAI9S,EACV8R,IAAYlR,KAAKvB,IAAQ0S,GAAcnR,KAAKvB,KAOhDsE,EAAMkP,EAAWxT,GACboE,EAAcE,KAChBkP,EAAWxT,GAAOJ,GAAIkE,OAAOQ,MAarC,QAASoP,IAAWnB,GAClB,GACI5R,GAAGV,EADH0T,EAAQpB,EAAQoB,KAEpB,IAAIC,GAAQD,GAGV,IAFApB,EAAQoB,SACRhT,EAAIgT,EAAM/S,OACHD,KACLV,EAAM0T,EAAMhT,GACO,gBAARV,GACTsS,EAAQoB,MAAM1T,GAAO,KACZA,EAAI2I,OACb2J,EAAQoB,MAAM1T,EAAI2I,MAAQ3I,OAGzB,IAAImE,EAAcuP,GAAQ,CAC/B,GAAI1P,GAAOC,OAAOD,KAAK0P,EAEvB,KADAhT,EAAIsD,EAAKrD,OACFD,KACLV,EAAM0T,EAAM1P,EAAKtD,IACE,kBAARV,KACT0T,EAAM1P,EAAKtD,KAAQkT,KAAM5T,KAcjC,QAASqT,IAAiBQ,GACxB,GAAIF,GAAQE,GAAS,CAInB,IAHA,GAEIC,GAFAV,KACA1S,EAAImT,EAAOlT,OAERD,KAAK,CACVoT,EAAQD,EAAOnT,EACf,IAAIkS,GAAsB,kBAAVkB,GAAuBA,EAAMxB,SAAWwB,EAAMxB,QAAQ3J,MAAQmL,EAAMlB,GAAKkB,EAAMnL,MAAQmL,EAAMlB,EACxGA,KAGHQ,EAAIR,GAAMkB,GAGd,MAAOV,GAET,MAAOS,GAaT,QAASE,IAAazG,EAAQyC,EAAOnP,GAwBnC,QAASoT,GAAWjU,GAClB,GAAIkU,GAAQC,GAAOnU,IAAQoU,EAC3B7B,GAAQvS,GAAOkU,EAAM3G,EAAOvN,GAAMgQ,EAAMhQ,GAAMa,EAAIb,GAzBpDuT,GAAgBvD,GAChB0D,GAAW1D,EAEX,IACIhQ,GADAuS,IAKJ,IAHIvC,EAAe,UACjBzC,EAAqC,kBAArByC,GAAe,QAAmBgE,GAAazG,EAAQyC,EAAe,QAAEuC,QAAS1R,GAAMmT,GAAazG,EAAQyC,EAAe,QAAGnP,IAE5ImP,EAAMqE,OACR,IAAK,GAAI1T,GAAI,EAAG2C,EAAI0M,EAAMqE,OAAOzT,OAAQD,EAAI2C,EAAG3C,IAAK,CACnD,GAAI2T,GAAQtE,EAAMqE,OAAO1T,GACrB4T,EAAeD,EAAME,oBAAqB5U,IAAM0U,EAAM/B,QAAU+B,CACpE/G,GAASyG,GAAazG,EAAQgH,EAAc1T,GAGhD,IAAKb,IAAOuN,GACV0G,EAAWjU,EAEb,KAAKA,IAAOgQ,GACL9P,EAAOqN,EAAQvN,IAClBiU,EAAWjU,EAOf,OAAOuS,GAeT,QAASK,IAAaL,EAASsB,EAAMhB,EAAI4B,GAEvC,GAAkB,gBAAP5B,GAAX,CAGA,GACI6B,GADAZ,EAASvB,EAAQsB,GAEjBR,EAAMS,EAAOjB,IAEjBiB,EAAOY,EAAclS,EAASqQ,KAE9BiB,EAAOY,EAAYC,OAAO,GAAG9R,cAAgB6R,EAAYnS,MAAM,GAE/D,OAAO8Q,IAWT,QAASuB,MACP/U,KAAKgT,GAAKgC,KACVhV,KAAKiV,QAoIP,QAASC,IAAkB3R,GACzB4R,IAAgB,EAChB5R,IACA4R,IAAgB,EAalB,QAASC,IAASpT,GAIhB,GAHAhC,KAAKgC,MAAQA,EACbhC,KAAKW,IAAM,GAAIoU,IACftQ,EAAIzC,EAAO,SAAUhC,MACjB+T,GAAQ/R,GAAQ,CAClB,GAAIqT,GAAUC,GAAWC,GAAeC,EACxCH,GAAQrT,EAAOyT,GAAcC,IAC7B1V,KAAK2V,aAAa3T,OAElBhC,MAAK4V,KAAK5T,GA+Ed,QAASuT,IAAarJ,EAAQ2J,GAE5B3J,EAAO4J,UAAYD,EAYrB,QAASL,IAAYtJ,EAAQ2J,EAAKzR,GAChC,IAAK,GAAItD,GAAI,EAAG2C,EAAIW,EAAKrD,OAAQD,EAAI2C,EAAG3C,IAAK,CAC3C,GAAIX,GAAMiE,EAAKtD,EACf2D,GAAIyH,EAAQ/L,EAAK0V,EAAI1V,KAezB,QAAS4V,IAAQ/T,EAAOhB,GACtB,GAAKgB,GAA0B,gBAAVA,GAArB,CAGA,GAAIxB,EASJ,OARIH,GAAO2B,EAAO,WAAaA,EAAMvB,iBAAkB2U,IACrD5U,EAAKwB,EAAMvB,OACF0U,KAAkBpB,GAAQ/R,IAAUuC,EAAcvC,KAAWqC,OAAO2R,aAAahU,KAAWA,EAAM1B,SAC3GE,EAAK,GAAI4U,IAASpT,IAEhBxB,GAAMQ,GACRR,EAAGyV,MAAMjV,GAEJR,GAWT,QAAS0V,IAAehW,EAAKC,EAAKC,GAChC,GAAIO,GAAM,GAAIoU,IAEVoB,EAAW9R,OAAO+R,yBAAyBlW,EAAKC,EACpD,KAAIgW,GAAYA,EAAStR,gBAAiB,EAA1C,CAKA,GAAIwR,GAASF,GAAYA,EAASxM,IAC9B2M,EAASH,GAAYA,EAASlW,IAE9BsW,EAAUR,GAAQ3V,EACtBiE,QAAOM,eAAezE,EAAKC,GACzBuE,YAAY,EACZG,cAAc,EACd8E,IAAK,WACH,GAAI3H,GAAQqU,EAASA,EAAO/U,KAAKpB,GAAOE,CACxC,IAAI2U,GAAI7I,SACNvL,EAAI6V,SACAD,GACFA,EAAQ5V,IAAI6V,SAEVzC,GAAQ/R,IACV,IAAK,GAAIyU,GAAG3V,EAAI,EAAG2C,EAAIzB,EAAMjB,OAAQD,EAAI2C,EAAG3C,IAC1C2V,EAAIzU,EAAMlB,GACV2V,GAAKA,EAAEhW,QAAUgW,EAAEhW,OAAOE,IAAI6V,QAIpC,OAAOxU,IAET/B,IAAK,SAAwByW,GAC3B,GAAI1U,GAAQqU,EAASA,EAAO/U,KAAKpB,GAAOE,CACpCsW,KAAW1U,IAGXsU,EACFA,EAAOhV,KAAKpB,EAAKwW,GAEjBtW,EAAMsW,EAERH,EAAUR,GAAQW,GAClB/V,EAAIC,cA+EV,QAAS+V,IAAW5W,GAYlBA,EAAI4U,UAAUiC,MAAQ,SAAUlE,GAC9BA,EAAUA,MAEV1S,KAAK6W,IAAM,KACX7W,KAAKgN,QAAU0F,EAAQhF,OACvB1N,KAAK8W,MAAQ9W,KAAKgN,QAAUhN,KAAKgN,QAAQ8J,MAAQ9W,KACjDA,KAAK+W,aACL/W,KAAKgX,SACLhX,KAAKiX,QACLjX,KAAKkX,aACLlX,KAAKmX,eAGLnX,KAAKoX,KAAOC,KAGZrX,KAAKM,QAAS,EAGdN,KAAKsX,WACLtX,KAAKuX,gBAGLvX,KAAKwX,aAAc,EACnBxX,KAAKyX,UACLzX,KAAK0X,eACL1X,KAAK2X,aAAe,KAGpB3X,KAAK+M,YAAc/M,KAAK4X,aAAe5X,KAAK6X,SAAW7X,KAAK8X,YAAc9X,KAAK+X,kBAAoB/X,KAAKgY,eAAgB,EACxHhY,KAAKiY,UAAY,KAMjBjY,KAAKkY,SAAWxF,EAAQwF,UAAYlY,KAAKgN,QAOzChN,KAAKmY,OAASzF,EAAQyF,OAMtBnY,KAAKoY,MAAQ1F,EAAQ0F,MACjBpY,KAAKoY,OACPpY,KAAKoY,MAAMC,SAASzP,KAAK5I,MAIvBA,KAAKgN,SACPhN,KAAKgN,QAAQ+J,UAAUnO,KAAK5I,MAI9B0S,EAAU1S,KAAKsY,SAAWnE,GAAanU,KAAKuY,YAAY7F,QAASA,EAAS1S,MAG1EA,KAAKwY,aAILxY,KAAKO,SAGLP,KAAKyY,UAAU,QAGfzY,KAAK0Y,aAGL1Y,KAAK2Y,cAGL3Y,KAAKyY,UAAU,WAGX/F,EAAQzG,IACVjM,KAAK4Y,OAAOlG,EAAQzG,KAmF1B,QAAS4M,IAAgBC,GACvB,GAAWnS,SAAPmS,EACF,MAAO,KAGT,IAAIC,GAAOD,EAAGhX,WAAW,EAEzB,QAAQiX,GACN,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IACL,IAAK,IAEH,MAAOD,EAET,KAAK,IACL,IAAK,IAEH,MAAO,OAET,KAAK,IACL,IAAK,GACL,IAAK,IACL,IAAK,IACL,IAAK,KACL,IAAK,OACL,IAAK,MACL,IAAK,MAEH,MAAO,KAIX,MAAIC,IAAQ,IAAQA,GAAQ,KAAQA,GAAQ,IAAQA,GAAQ,GACnD,QAILA,GAAQ,IAAQA,GAAQ,GACnB,SAGF,OAYT,QAASC,IAAcC,GACrB,GAAIC,GAAUD,EAAKjQ,MAEnB,QAAuB,MAAnBiQ,EAAKnE,OAAO,KAAczS,MAAM4W,MAG7B1X,EAAU2X,GAAW3W,EAAY2W,GAAW,IAAMA,GAU3D,QAASC,IAAMF,GA6Cb,QAASG,KACP,GAAIC,GAAWJ,EAAKlS,EAAQ,EAC5B,IAAIuS,IAASC,IAAgC,MAAbF,GAAoBC,IAASE,IAAgC,MAAbH,EAI9E,MAHAtS,KACA0S,EAAU,KAAOJ,EACjBK,EAAQC,OACD,EAlDX,GAII9X,GAAG4X,EAAStZ,EAAK6T,EAAMrH,EAAYM,EAAQ2M,EAJ3CxV,KACA2C,GAAQ,EACRuS,EAAOO,GACPC,EAAe,EAGfJ,IAgDJ,KA9CAA,EAAQK,IAAQ,WACFpT,SAARxG,IACFiE,EAAKwE,KAAKzI,GACVA,EAAMwG,SAIV+S,EAAQC,IAAU,WACJhT,SAARxG,EACFA,EAAMsZ,EAENtZ,GAAOsZ,GAIXC,EAAQM,IAAsB,WAC5BN,EAAQC,MACRG,KAGFJ,EAAQO,IAAiB,WACvB,GAAIH,EAAe,EACjBA,IACAR,EAAOY,GACPR,EAAQC,UACH,CAGL,GAFAG,EAAe,EACf3Z,EAAM6Y,GAAc7Y,GAChBA,KAAQ,EACV,OAAO,CAEPuZ,GAAQK,QAeC,MAART,GAIL,GAHAvS,IACAlF,EAAIoX,EAAKlS,GAEC,OAANlF,IAAcuX,IAAlB,CAQA,GAJApF,EAAO6E,GAAgBhX,GACvB+X,EAAUO,GAAiBb,GAC3B3M,EAAaiN,EAAQ5F,IAAS4F,EAAc,MAAKQ,GAE7CzN,IAAeyN,GACjB,MAKF,IAFAd,EAAO3M,EAAW,GAClBM,EAASyM,EAAQ/M,EAAW,IACxBM,IACFwM,EAAU9M,EAAW,GACrB8M,EAAsB9S,SAAZ8S,EAAwB5X,EAAI4X,EAClCxM,OAAa,GACf,MAIJ,IAAIqM,IAASe,GAEX,MADAjW,GAAKkW,IAAMrB,EACJ7U,GAYb,QAASmW,IAAUtB,GACjB,GAAIxP,GAAM+Q,GAAU7Q,IAAIsP,EAOxB,OANKxP,KACHA,EAAM0P,GAAMF,GACRxP,GACF+Q,GAAU1Q,IAAImP,EAAMxP,IAGjBA,EAUT,QAASgR,IAAQva,EAAK+Y,GACpB,MAAOyB,IAAkBzB,GAAMtP,IAAIzJ,GAWrC,QAASya,IAAQza,EAAK+Y,EAAM7Y,GAC1B,GAAIwa,GAAW1a,CAIf,IAHoB,gBAAT+Y,KACTA,EAAOE,GAAMF,KAEVA,IAAS3U,EAASpE,GACrB,OAAO,CAGT,KAAK,GADDqF,GAAMpF,EACDW,EAAI,EAAG2C,EAAIwV,EAAKlY,OAAQD,EAAI2C,EAAG3C,IACtCyE,EAAOrF,EACPC,EAAM8Y,EAAKnY,GACW,MAAlBX,EAAI2U,OAAO,KACb3U,EAAMua,GAAkBva,EAAIuC,MAAM,IAAIiH,IAAIrI,KAAKsZ,EAAUA,IAEvD9Z,EAAI2C,EAAI,GACVvD,EAAMA,EAAIC,GACLmE,EAASpE,KACZA,KAEAD,EAAIsF,EAAMpF,EAAKD,KAGb6T,GAAQ7T,GACVA,EAAI2a,KAAK1a,EAAKC,GACLD,IAAOD,GAChBA,EAAIC,GAAOC,EAGXH,EAAIC,EAAKC,EAAKC,EAIpB,QAAO,EA0BT,QAAS0a,OA6BT,QAASC,IAAKnZ,EAAKoZ,GACjB,GAAIla,GAAIma,GAAMla,MAEd,OADAka,IAAMna,GAAKka,EAAWpZ,EAAIgB,QAAQsY,GAAW,OAAStZ,EAC/C,IAAMd,EAAI,IAUnB,QAASqa,IAAQb,GACf,GAAIzY,GAAIyY,EAAIxF,OAAO,GACfmE,EAAOqB,EAAI5X,MAAM,EACrB,OAAI0Y,IAAkB1Z,KAAKuX,GAClBqB,GAEPrB,EAAOA,EAAKtT,QAAQ,MAAO,EAAKsT,EAAKrW,QAAQyY,GAAWC,IAAWrC,EAC5DpX,EAAI,SAAWoX,GAY1B,QAASqC,IAAQ1Z,EAAKd,GACpB,MAAOma,IAAMna,GAWf,QAASya,IAAc/Z,GACjBga,GAAmB9Z,KAAKF,GAI5ByZ,GAAMla,OAAS,CAEf,IAAI0a,GAAOja,EAAIoB,QAAQ8Y,GAAQX,IAAMnY,QAAQ+Y,GAAM,GAInD,OADAF,IAAQ,IAAMA,GAAM7Y,QAAQgZ,GAAST,IAASvY,QAAQyY,GAAWC,IAC1DO,GAAaJ,GAatB,QAASI,IAAaJ,GACpB,IAEE,MAAO,IAAIK,UAAS,QAAS,UAAYL,EAAO,KAEhD,MAAOhF,GAEP,MAAOqE,KAWX,QAASiB,IAAcva,GACrB,GAAIyX,GAAOsB,GAAU/Y,EACrB,IAAIyX,EACF,MAAO,UAAU+C,EAAO5b,GACtBua,GAAQqB,EAAO/C,EAAM7Y,IAe3B,QAASsa,IAAkBlZ,EAAKya,GAC9Bza,EAAMA,EAAIwH,MAEV,IAAIS,GAAMyS,GAAgBvS,IAAInI,EAC9B,IAAIiI,EAIF,MAHIwS,KAAYxS,EAAIxJ,MAClBwJ,EAAIxJ,IAAM8b,GAActS,EAAIjI,MAEvBiI,CAET,IAAI+J,IAAQhS,IAAKA,EAUjB,OATAgS,GAAI7J,IAAMwS,GAAa3a,IAAQA,EAAImE,QAAQ,KAAO,EAEhDkW,GAAa,SAAWra,GAExB+Z,GAAc/Z,GACZya,IACFzI,EAAIvT,IAAM8b,GAAcva,IAE1B0a,GAAgBpS,IAAItI,EAAKgS,GAClBA,EAUT,QAAS2I,IAAa3a,GACpB,MAAO4a,IAAW1a,KAAKF,KAEtB6a,GAAiB3a,KAAKF,IAEH,UAApBA,EAAIkB,MAAM,EAAG,GAyBf,QAAS4Z,MACPC,GAAMxb,OAAS,EACfyb,GAAUzb,OAAS,EACnB0b,MACAC,MACAC,IAAU,EAOZ,QAASC,MAGI,IAFX,GAAIC,IAAS,EAEKA,GAChBA,GAAS,EAETC,GAAgBP,IAChBO,GAAgBN,IAGZD,GAAMxb,OACR8b,GAAS,GAKPE,IAAY5S,GAAO4S,UACrBA,GAASC,KAAK,SAEhBV,MAUJ,QAASQ,IAAgBP,GAGvB,IAAK,GAAIzb,GAAI,EAAGA,EAAIyb,EAAMxb,OAAQD,IAAK,CACrC,GAAImc,GAAUV,EAAMzb,GAChBkS,EAAKiK,EAAQjK,EACjByJ,IAAIzJ,GAAM,KACViK,EAAQC,MAIVX,EAAMxb,OAAS,EAcjB,QAASoc,IAAYF,GACnB,GAAIjK,GAAKiK,EAAQjK,EACjB,IAAe,MAAXyJ,GAAIzJ,GAAa,CAEnB,GAAIoK,GAAIH,EAAQI,KAAOb,GAAYD,EACnCE,IAAIzJ,GAAMoK,EAAErc,OACZqc,EAAExU,KAAKqU,GAEFN,KACHA,IAAU,EACVW,GAASV,MA0Bf,QAASW,IAAQvc,EAAIwc,EAAS1X,EAAI4M,GAE5BA,GACFzO,EAAOjE,KAAM0S,EAEf,IAAI+K,GAA0B,kBAAZD,EAclB,IAbAxd,KAAKgB,GAAKA,EACVA,EAAGkW,UAAUtO,KAAK5I,MAClBA,KAAK6J,WAAa2T,EAClBxd,KAAK8F,GAAKA,EACV9F,KAAKgT,KAAO0K,GACZ1d,KAAK2d,QAAS,EACd3d,KAAK4d,MAAQ5d,KAAK6d,KAClB7d,KAAK8d,QACL9d,KAAK+d,WACL/d,KAAKge,OAAS,GAAIC,IAClBje,KAAKke,UAAY,GAAID,IACrBje,KAAKme,UAAY,KAEbV,EACFzd,KAAKqW,OAASmH,EACdxd,KAAKsW,OAAS3P,WACT,CACL,GAAI6M,GAAMkH,GAAkB8C,EAASxd,KAAKoe,OAC1Cpe,MAAKqW,OAAS7C,EAAI7J,IAClB3J,KAAKsW,OAAS9C,EAAIvT,IAEpBD,KAAKgC,MAAQhC,KAAK6d,KAAOlX,OAAY3G,KAAK2J,MAG1C3J,KAAKqe,OAASre,KAAKse,SAAU,EAkO/B,QAASC,IAASne,EAAKoe,GACrB,GAAI1d,GAAI6F,OACJvC,EAAOuC,MACN6X,KACHA,EAAOC,GACPD,EAAKE,QAEP,IAAIC,GAAM5K,GAAQ3T,GACdwe,EAAMta,EAASlE,EACnB,KAAKue,GAAOC,IAAQva,OAAO2R,aAAa5V,GAAM,CAC5C,GAAIA,EAAIK,OAAQ,CACd,GAAIoe,GAAQze,EAAIK,OAAOE,IAAIqS,EAC3B,IAAIwL,EAAK/B,IAAIoC,GACX,MAEAL,GAAK3O,IAAIgP,GAGb,GAAIF,EAEF,IADA7d,EAAIV,EAAIW,OACDD,KAAKyd,GAASne,EAAIU,GAAI0d,OACxB,IAAII,EAGT,IAFAxa,EAAOC,OAAOD,KAAKhE,GACnBU,EAAIsD,EAAKrD,OACFD,KAAKyd,GAASne,EAAIgE,EAAKtD,IAAK0d,IA0CzC,QAASM,IAAexR,GACtB,MAAO+C,IAAW/C,IAASgD,GAAWhD,EAAKiD,SAiB7C,QAASwO,IAAiBC,EAAgB1E,GAExC,GAAI2E,GAAW3E,EAAM0E,EAAiBA,EAAehW,OACjDS,EAAMyV,GAAcvV,IAAIsV,EAC5B,IAAIxV,EACF,MAAOA,EAGT,IAAIsI,GAAO5E,SAASuD,yBAChByO,EAAWH,EAAejU,MAAMqU,IAChCC,EAAcC,GAAS5d,KAAKsd,GAC5BO,EAAeC,GAAU9d,KAAKsd,EAElC,IAAKG,GAAaE,GAAgBE,EAG3B,CACL,GAAIjU,GAAM6T,GAAYA,EAAS,GAC3BM,EAAOjU,GAAIF,IAAQE,GAAIkU,OACvBC,EAAQF,EAAK,GACbG,EAASH,EAAK,GACdI,EAASJ,EAAK,GACdnS,EAAOH,SAASwD,cAAc,MAGlC,KADArD,EAAKkF,UAAYoN,EAASZ,EAAiBa,EACpCF,KACLrS,EAAOA,EAAKuD,SAKd,KAFA,GAAIV,GAEGA,EAAQ7C,EAAKoB,YAElBqD,EAAK3F,YAAY+D,OAlBnB4B,GAAK3F,YAAYe,SAASkE,eAAe2N,GAyB3C,OAJK1E,IACH7J,GAASsB,GAEXmN,GAAcpV,IAAImV,EAAUlN,GACrBA,EAUT,QAAS+N,IAAexS,GAOtB,GAAIwR,GAAexR,GACjB,MAAOyR,IAAiBzR,EAAKkF,UAG/B,IAAqB,WAAjBlF,EAAKyD,QACP,MAAOgO,IAAiBzR,EAAKyS,YAO/B,KAJA,GAEI5P,GAFA6P,EAAazN,GAAUjF,GACvByE,EAAO5E,SAASuD,yBAGbP,EAAQ6P,EAAWtR,YAExBqD,EAAK3F,YAAY+D,EAGnB,OADAM,IAASsB,GACFA,EAsCT,QAASQ,IAAUjF,GAEjB,IAAKA,EAAK2S,iBACR,MAAO3S,GAAKiF,WAEd,IACIzR,GAAG8Z,EAAUsF,EADb1M,EAAMlG,EAAKiF,WAAU,EAGzB,IAAI4N,GAAmB,CACrB,GAAIC,GAAY5M,CAMhB,IALIsL,GAAexR,KACjBA,EAAOA,EAAKiD,QACZ6P,EAAY5M,EAAIjD,SAElBqK,EAAWtN,EAAK2S,iBAAiB,YAC7BrF,EAAS7Z,OAGX,IAFAmf,EAASE,EAAUH,iBAAiB,YACpCnf,EAAIof,EAAOnf,OACJD,KACLof,EAAOpf,GAAG6M,WAAWgB,aAAa4D,GAAUqI,EAAS9Z,IAAKof,EAAOpf,IAKvE,GAAIuf,GACF,GAAqB,aAAjB/S,EAAKyD,QACPyC,EAAIxR,MAAQsL,EAAKtL,UAGjB,IADA4Y,EAAWtN,EAAK2S,iBAAiB,YAC7BrF,EAAS7Z,OAGX,IAFAmf,EAAS1M,EAAIyM,iBAAiB,YAC9Bnf,EAAIof,EAAOnf,OACJD,KACLof,EAAOpf,GAAGkB,MAAQ4Y,EAAS9Z,GAAGkB,KAKtC,OAAOwR,GAqBT,QAAS8M,IAAcC,EAAUC,EAAalG,GAC5C,GAAIhN,GAAMyE,CAIV,OAAIzB,IAAWiQ,IACb9P,GAAS8P,GACFC,EAAcjO,GAAUgO,GAAYA,IAGrB,gBAAbA,GAEJjG,GAA8B,MAAvBiG,EAASzL,OAAO,GAa1B/C,EAAOgN,GAAiBwB,EAAUjG,IAXlCvI,EAAO0O,GAAgB9W,IAAI4W,GACtBxO,IACHzE,EAAOH,SAASuT,eAAeH,EAAS7d,MAAM,IAC1C4K,IACFyE,EAAO+N,GAAexS,GAEtBmT,GAAgB3W,IAAIyW,EAAUxO,MAO3BwO,EAAS3S,WAElBmE,EAAO+N,GAAeS,IAGjBxO,GAAQyO,EAAcjO,GAAUR,GAAQA,GAyDjD,QAAS4O,IAASC,EAAQ5f,EAAI+Q,EAAM8O,EAAM7E,EAAO8E,GAC/C9gB,KAAKqY,YACLrY,KAAK+gB,cACL/gB,KAAKgB,GAAKA,EACVhB,KAAKgc,MAAQA,EACbhc,KAAKghB,UAAW,EAChBhhB,KAAK8gB,WAAaA,EACdA,GACFA,EAAWC,WAAWnY,KAAK5I,MAE7BA,KAAKihB,OAASL,EAAO5f,EAAI+Q,EAAM8O,EAAM7E,EAAOhc,KAC5C,IAAI4L,GAAS5L,KAAK4L,OAAoC,IAA3BmG,EAAKmP,WAAWngB,SAE1CgR,EAAKmP,WAAW,GAAG5P,UAChB1F,IACF5L,KAAKsN,KAAOyE,EAAKmP,WAAW,GAC5BlhB,KAAKsM,OAAS6U,GACdnhB,KAAKwM,OAAS4U,KAEdphB,KAAKsN,KAAO0D,GAAa,kBACzBhR,KAAK6R,IAAMb,GAAa,gBACxBhR,KAAK+R,KAAOA,EACZtD,GAAQzO,KAAKsN,KAAMyE,GACnBA,EAAK3F,YAAYpM,KAAK6R,KACtB7R,KAAKsM,OAAS+U,GACdrhB,KAAKwM,OAAS8U,IAEhBthB,KAAKsN,KAAKiU,SAAWvhB,KA4BvB,QAASmhB,IAAajV,EAAQsV,GAC5BxhB,KAAKghB,UAAW,CAChB,IAAIS,GAASD,KAAmB,EAAQnV,EAAuBC,EAC/DmV,GAAOzhB,KAAKsN,KAAMpB,EAAQlM,KAAKgB,IAC3BqM,EAAMrN,KAAKsN,OACbtN,KAAK0hB,SAASC,IAQlB,QAASP,MACPphB,KAAKghB,UAAW,CAChB,IAAIY,GAAmBvU,EAAMrN,KAAKsN,MAC9BuU,EAAO7hB,IACXA,MAAK8hB,eACLvV,EAAqBvM,KAAKsN,KAAMtN,KAAKgB,GAAI,WACnC4gB,GACFC,EAAKH,SAASK,IAEhBF,EAAKG,YAWT,QAASX,IAAYnV,EAAQsV,GAC3BxhB,KAAKghB,UAAW,CAChB,IAAIhgB,GAAKhB,KAAKgB,GACVygB,EAASD,KAAmB,EAAQnV,EAAuBC,EAC/DsF,IAAa5R,KAAKsN,KAAMtN,KAAK6R,IAAK,SAAUvE,GAC1CmU,EAAOnU,EAAMpB,EAAQlL,KAEnBqM,EAAMrN,KAAKsN,OACbtN,KAAK0hB,SAASC,IAQlB,QAASL,MACPthB,KAAKghB,UAAW,CAChB,IAAIa,GAAO7hB,KACP4hB,EAAmBvU,EAAMrN,KAAKsN,KAClCtN,MAAK8hB,eACLhQ,GAAgB9R,KAAKsN,KAAMtN,KAAK6R,IAAK7R,KAAKgB,GAAIhB,KAAK+R,KAAM,WACnD6P,GACFC,EAAKH,SAASK,IAEhBF,EAAKG,YAkDT,QAASL,IAAOxR,IACTA,EAAM2H,aAAezK,EAAM8C,EAAM0G,MACpC1G,EAAMsI,UAAU,YAUpB,QAASsJ,IAAO5R,GACVA,EAAM2H,cAAgBzK,EAAM8C,EAAM0G,MACpC1G,EAAMsI,UAAU,YAapB,QAASwJ,IAAgBjhB,EAAIiL,GAC3BjM,KAAKgB,GAAKA,CACV,IAAIuf,GACAvF,EAAyB,gBAAP/O,EAClB+O,IAAY3K,GAAWpE,KAAQA,EAAGmC,aAAa,QACjDmS,EAAWD,GAAcrU,GAAI,IAE7BsU,EAAWpT,SAASuD,yBACpB6P,EAASnU,YAAYH,IAEvBjM,KAAKugB,SAAWA,CAEhB,IAAIK,GACAsB,EAAMlhB,EAAGuX,YAAY2J,GACzB,IAAIA,EAAM,EAAG,CACX,GAAIC,GAAUD,GAAOlH,EAAW/O,EAAKmG,GAAanG,GAClD2U,GAASwB,GAAYzY,IAAIwY,GACpBvB,IACHA,EAASyB,GAAQ9B,EAAUvf,EAAGsX,UAAU,GACxC8J,GAAYtY,IAAIqY,EAASvB,QAG3BA,GAASyB,GAAQ9B,EAAUvf,EAAGsX,UAAU,EAE1CtY,MAAK4gB,OAASA,EAujBhB,QAAS0B,IAAavQ,EAAMb,EAAQ8B,GAClC,GAAI/G,GAAK8F,EAAKzE,KAAKiV,eAEnB,IAAKtW,EAAL,CAEA,IADA8F,EAAO9F,EAAGsV,WACDxP,GAAQA,EAAKyQ,QAAUxP,GAAOjB,EAAKiP,UAAa/U,IAAOiF,IAAQ,CAGtE,GAFAjF,EAAKA,EAAGsW,iBAEHtW,EAAI,MACT8F,GAAO9F,EAAGsV,SAEZ,MAAOxP,IAUT,QAAS0Q,IAAMC,GAGb,IAFA,GAAI5hB,IAAI,EACJiD,EAAM,GAAIC,OAAM2e,KAAKC,MAAMF,MACtB5hB,EAAI4hB,GACX3e,EAAIjD,GAAKA,CAEX,OAAOiD,GAYT,QAAS8e,IAAc9b,EAAO5G,EAAK6B,EAAO8gB,GACxC,MAAOA,GAA4B,WAAfA,EAA0B/b,EAAQ+b,EAAWhO,OAAO,GAAG/J,MAAM,MAAQ0P,GAAQzY,EAAO8gB,GAAc9gB,EAAM8gB,GAAc3iB,GAAO6B,EAUnJ,QAAS+gB,IAAehR,GACtB,GAAIzE,GAAOyE,EAAKzE,IAEhB,IAAIyE,EAAKF,IACP,MAAQvE,EAAK0V,SAAW1V,IAASyE,EAAKF,KAAOvE,EAAKiB,aAChDjB,EAAOA,EAAKiB,WAGhB,OAAOjB,GAAK0V,QAsVd,QAASC,IAAShX,EAAIiX,EAAOC,GAG3B,IAAK,GADDzW,GAAItM,EAAKgjB,EADT5P,EAAM0P,KAAa,KAEdpiB,EAAI,EAAG2C,EAAIwI,EAAGyG,QAAQ3R,OAAQD,EAAI2C,EAAG3C,IAG5C,GAFA4L,EAAKT,EAAGyG,QAAQ5R,GAChBsiB,EAAWD,EAAOzW,EAAG0B,aAAa,YAAc1B,EAAG0W,SACrC,CAEZ,GADAhjB,EAAMsM,EAAGrL,eAAe,UAAYqL,EAAG2W,OAAS3W,EAAG1K,OAC/CkhB,EAGF,MAAO9iB,EAFPoT,GAAI5K,KAAKxI,GAMf,MAAOoT,GAWT,QAAS8P,IAAU1d,EAAKxF,GAEtB,IADA,GAAIU,GAAI8E,EAAI7E,OACLD,KACL,GAAImF,EAAWL,EAAI9E,GAAIV,GACrB,MAAOU,EAGX,QAAO,EAqJT,QAASyiB,IAAUC,EAASpf,GAC1B,GAAIqf,GAAQrf,EAAKoH,IAAI,SAAUrL,GAC7B,GAAIujB,GAAWvjB,EAAI2B,WAAW,EAC9B,OAAI4hB,GAAW,IAAMA,EAAW,GACvBC,SAASxjB,EAAK,IAEJ,IAAfA,EAAIY,SACN2iB,EAAWvjB,EAAI6C,cAAclB,WAAW,GACpC4hB,EAAW,IAAMA,EAAW,IACvBA,EAGJE,GAASzjB,IAGlB,OADAsjB,MAAWI,OAAOlgB,SAAU8f,GACrB,SAAoBhN,GACzB,GAAIgN,EAAM9d,QAAQ8Q,EAAEqN,UAAW,EAC7B,MAAON,GAAQliB,KAAKtB,KAAMyW,IAKhC,QAASsN,IAAWP,GAClB,MAAO,UAAqB/M,GAE1B,MADAA,GAAEuN,kBACKR,EAAQliB,KAAKtB,KAAMyW,IAI9B,QAASwN,IAAcT,GACrB,MAAO,UAAwB/M,GAE7B,MADAA,GAAEyN,iBACKV,EAAQliB,KAAKtB,KAAMyW,IAI9B,QAAS0N,IAAWX,GAClB,MAAO,UAAqB/M,GAC1B,GAAIA,EAAEvK,SAAWuK,EAAE2N,cACjB,MAAOZ,GAAQliB,KAAKtB,KAAMyW,IAmJhC,QAAS4N,IAAUC,GACjB,GAAIC,GAAUD,GACZ,MAAOC,IAAUD,EAEnB,IAAI9Q,GAAMoM,GAAO0E,EAEjB,OADAC,IAAUD,GAAQC,GAAU/Q,GAAOA,EAC5BA,EAWT,QAASoM,IAAO0E,GACdA,EAAOrhB,EAAUqhB,EACjB,IAAIE,GAAQ7hB,EAAS2hB,GACjBG,EAAQD,EAAM1P,OAAO,GAAG9R,cAAgBwhB,EAAM9hB,MAAM,EACnDgiB,MACHA,GAASvX,SAASwD,cAAc,OAElC,IACIgU,GADA7jB,EAAI8jB,GAAS7jB,MAEjB,IAAc,WAAVyjB,GAAsBA,IAASE,IAAOG,MACxC,OACEC,MAAOR,EACPE,MAAOA,EAGX,MAAO1jB,KAEL,GADA6jB,EAAWI,GAAcjkB,GAAK2jB,EAC1BE,IAAYD,IAAOG,MACrB,OACEC,MAAOF,GAAS9jB,GAAKwjB,EACrBE,MAAOG,GAyOf,QAASK,IAAYhjB,GACnB,GAAIwR,KACJ,IAAIO,GAAQ/R,GACV,IAAK,GAAIlB,GAAI,EAAG2C,EAAIzB,EAAMjB,OAAQD,EAAI2C,EAAG3C,IAAK,CAC5C,GAAImkB,GAAOjjB,EAAMlB,EACjB,IAAImkB,EACF,GAAoB,gBAATA,GACTzR,EAAI5K,KAAKqc,OAET,KAAK,GAAIC,KAAKD,GACRA,EAAKC,IAAI1R,EAAI5K,KAAKsc,OAKzB,IAAI5gB,EAAStC,GAClB,IAAK,GAAI7B,KAAO6B,GACVA,EAAM7B,IAAMqT,EAAI5K,KAAKzI,EAG7B,OAAOqT,GAcT,QAAS7P,IAAMsI,EAAI9L,EAAKoD,GAEtB,GADApD,EAAMA,EAAI6I,OACN7I,EAAIwF,QAAQ,QAAS,EAEvB,WADApC,GAAG0I,EAAI9L,EAOT,KAAK,GADDiE,GAAOjE,EAAIglB,MAAM,OACZrkB,EAAI,EAAG2C,EAAIW,EAAKrD,OAAQD,EAAI2C,EAAG3C,IACtCyC,EAAG0I,EAAI7H,EAAKtD,IA+VhB,QAASskB,IAAkBvY,EAAO7L,EAAI8E,GAIpC,QAASkB,OACDqe,GAAUC,EACdxf,IAEA+G,EAAMwY,GAAQ/jB,KAAKN,EAAIgG,GAP3B,GAAIse,GAAQzY,EAAM9L,OACdskB,EAAS,CACbxY,GAAM,GAAGvL,KAAKN,EAAIgG,GA2BpB,QAASue,IAAatZ,EAAIuZ,EAAaxkB,GAMrC,IALA,GAII0R,GAAS3J,EAAM0c,EAAMzjB,EAAOiX,EAAM9W,EAAQmiB,EAJ1CxQ,KACA4R,EAAY1kB,EAAGsX,SAASoN,UACxBC,EAAQthB,OAAOD,KAAKohB,GACpB1kB,EAAI6kB,EAAM5kB,OAEPD,KACLiI,EAAO4c,EAAM7kB,GACb4R,EAAU8S,EAAYzc,IAAS6c,GAO/B3M,EAAOtW,EAASoG,GACX8c,GAAUnkB,KAAKuX,KAKpBqL,GACEvb,KAAMA,EACNkQ,KAAMA,EACNvG,QAASA,EACT4G,KAAMwM,GAAiBC,QACvBzL,IAAK,MAGPmL,EAAOxiB,EAAU8F,GAEuB,QAAnC/G,EAAQkM,EAAYjC,EAAIwZ,MACuB,QAA7CzjB,EAAQkM,EAAYjC,EAAIwZ,EAAO,UAClCnB,EAAKhL,KAAOwM,GAAiBE,QAC0B,QAA7ChkB,EAAQkM,EAAYjC,EAAIwZ,EAAO,YACzCnB,EAAKhL,KAAOwM,GAAiBG,WAGnB,OAAVjkB,GAEFsiB,EAAKhK,IAAMtY,EACXG,EAASoH,EAAevH,GACxBA,EAAQG,EAAO0H,WACfya,EAAK3b,QAAUxG,EAAOwG,QAElBpH,EAAUS,KAAWG,EAAOwG,QAI9B2b,EAAK4B,kBAAmB,EAExB5B,EAAKjb,SAAU,EAIjBib,EAAK6B,WAAankB,GAIuB,QAA/BA,EAAQ8L,EAAQ7B,EAAIwZ,IAE9BnB,EAAKhK,IAAMtY,EACF0jB,GAA8D,QAAhD1jB,EAAQ0jB,EAAU3c,IAAS2c,EAAUzM,MAE5DqL,EAAKhK,IAAMtY,GAGb8R,EAAMlL,KAAK0b,GAEb,OAAO8B,IAAgBtS,GAUzB,QAASsS,IAAgBtS,GACvB,MAAO,UAAqB9S,EAAIgb,GAE9Bhb,EAAGqlB,SAIH,KAHA,GAEI/B,GAAMrL,EAAMvG,EAAS1Q,EAAOsY,EAF5BgM,EAActlB,EAAGsX,SAASoN,UAC1B5kB,EAAIgT,EAAM/S,OAEPD,KAQJ,GAPDwjB,EAAOxQ,EAAMhT,GACbwZ,EAAMgK,EAAKhK,IACXrB,EAAOqL,EAAKrL,KACZvG,EAAU4R,EAAK5R,QACf1R,EAAGqlB,OAAOpN,GAAQqL,EACdgC,GAAejmB,EAAOimB,EAAarN,IACrCsN,GAASvlB,EAAIsjB,EAAMgC,EAAYrN,IACpB,OAARqB,EAEHiM,GAASvlB,EAAIsjB,EAAM3d,YACd,IAAI2d,EAAKjb,QAEVib,EAAKhL,OAASwM,GAAiBG,UAEjCjkB,GAASga,GAAShb,EAAGkX,UAAYlX,GAAIwlB,KAAKlC,EAAK6B,YAC/CI,GAASvlB,EAAIsjB,EAAMtiB,IAEfhB,EAAGkX,SAELlX,EAAGylB,UACD1d,KAAM,OACNtE,IAAKiiB,GACLpC,KAAMA,GACL,KAAM,KAAMtI,GAGbuK,GAASvlB,EAAIsjB,EAAMtjB,EAAGwlB,KAAKlC,EAAK6B,iBAGjC,IAAI7B,EAAK4B,iBAAkB,CAEhC,GAAI5c,GAAW/G,EAAY+X,EAC3BtY,GAAQsH,IAAagR,EAAMhY,EAAUJ,EAASoY,IAAQhR,EACtDid,GAASvlB,EAAIsjB,EAAMtiB,OAMnBA,GAAQ0Q,EAAQsB,OAAS2S,UAAoB,KAARrM,GAAcA,IAAQrX,EAAUqhB,EAAKvb,QAAgBuR,EAC1FiM,GAASvlB,EAAIsjB,EAAMtiB,IAiB3B,QAAS4kB,IAAiB5lB,EAAIsjB,EAAMuC,EAAUtjB,GAC5C,GAAIujB,GAAWxC,EAAKjb,SAAW8S,GAAamI,EAAK6B,YAC7CnkB,EAAQ6kB,CACElgB,UAAV3E,IACFA,EAAQ+kB,GAAoB/lB,EAAIsjB,IAElCtiB,EAAQglB,GAAW1C,EAAMtiB,EAAOhB,EAChC,IAAIimB,GAAUjlB,IAAU6kB,CACnBK,IAAW5C,EAAMtiB,EAAOhB,KAC3BgB,EAAQ2E,QAENmgB,IAAaG,EACf/R,GAAkB,WAChB3R,EAAGvB,KAGLuB,EAAGvB,GAYP,QAASukB,IAASvlB,EAAIsjB,EAAMtiB,GAC1B4kB,GAAiB5lB,EAAIsjB,EAAMtiB,EAAO,SAAUA,GAC1CkU,GAAelV,EAAIsjB,EAAKrL,KAAMjX,KAYlC,QAASmlB,IAAWnmB,EAAIsjB,EAAMtiB,GAC5B4kB,GAAiB5lB,EAAIsjB,EAAMtiB,EAAO,SAAUA,GAC1ChB,EAAGsjB,EAAKrL,MAAQjX,IAYpB,QAAS+kB,IAAoB/lB,EAAIsjB,GAE/B,GAAI5R,GAAU4R,EAAK5R,OACnB,KAAKrS,EAAOqS,EAAS,WAEnB,MAAOA,GAAQsB,OAAS2S,SAAkBhgB,MAE5C,IAAIlC,GAAMiO,EAAiB,OAM3B,OAJIpO,GAASG,GAIS,kBAARA,IAAsBiO,EAAQsB,OAAS8H,SAAWrX,EAAInD,KAAKN,GAAMyD,EAWjF,QAASyiB,IAAW5C,EAAMtiB,EAAOhB,GAC/B,IAAKsjB,EAAK5R,QAAQ0U,WACL,OAAb9C,EAAKhK,KACI,MAATtY,GAEI,OAAO,CAEX,IAAI0Q,GAAU4R,EAAK5R,QACfsB,EAAOtB,EAAQsB,KACfqT,GAASrT,EACTsT,IACJ,IAAItT,EAAM,CACHD,GAAQC,KACXA,GAAQA,GAEV,KAAK,GAAIlT,GAAI,EAAGA,EAAIkT,EAAKjT,SAAWsmB,EAAOvmB,IAAK,CAC9C,GAAIymB,GAAeC,GAAWxlB,EAAOgS,EAAKlT,GAC1CwmB,GAAc1e,KAAK2e,EAAaE,cAChCJ,EAAQE,EAAaF,OAGzB,IAAKA,EAEH,OAAO,CAET,IAAIK,GAAYhV,EAAQgV,SACxB,SAAIA,IACGA,EAAU1lB,IAgBnB,QAASglB,IAAW1C,EAAMtiB,EAAOhB,GAC/B,GAAI2mB,GAASrD,EAAK5R,QAAQiV,MAC1B,OAAKA,IAGiB,kBAAXA,GACFA,EAAO3lB,GAHPA,EAkBX,QAASwlB,IAAWxlB,EAAOgS,GACzB,GAAIqT,GACAI,CAsBJ,OArBIzT,KAAS4T,QACXH,EAAe,SACfJ,QAAerlB,KAAUylB,GAChBzT,IAAS5R,QAClBqlB,EAAe,SACfJ,QAAerlB,KAAUylB,GAChBzT,IAAS2S,SAClBc,EAAe,UACfJ,QAAerlB,KAAUylB,GAChBzT,IAAS8H,UAClB2L,EAAe,WACfJ,QAAerlB,KAAUylB,GAChBzT,IAAS3P,QAClBojB,EAAe,SACfJ,EAAQ9iB,EAAcvC,IACbgS,IAAShQ,OAClByjB,EAAe,QACfJ,EAAQtT,GAAQ/R,IAEhBqlB,EAAQrlB,YAAiBgS,IAGzBqT,MAAOA,EACPI,aAAcA,GAiElB,QAASI,IAAQC,GACfC,GAAQnf,KAAKkf,GACRzJ,KACHA,IAAS,EACTf,GAAS0K,KASb,QAASA,MAGP,IAAK,GADDC,GAAI9a,SAASM,gBAAgBya,aACxBpnB,EAAI,EAAGA,EAAIinB,GAAQhnB,OAAQD,IAClCinB,GAAQjnB,IAMV,OAJAinB,OACA1J,IAAS,EAGF4J,EA2CT,QAASE,IAAWlc,EAAI+G,EAAInG,EAAO7L,GACjChB,KAAKgT,GAAKA,EACVhT,KAAKiM,GAAKA,EACVjM,KAAKooB,WAAavb,GAASA,EAAMub,YAAcpV,EAAK,SACpDhT,KAAKqoB,WAAaxb,GAASA,EAAMwb,YAAcrV,EAAK,SACpDhT,KAAK6M,MAAQA,EACb7M,KAAKgB,GAAKA,EAEVhB,KAAKsoB,gBAAkBtoB,KAAKuoB,aAAevoB,KAAKgG,OAAShG,KAAKwoB,YAAcxoB,KAAK0M,GAAK1M,KAAK8F,GAAK,KAChG9F,KAAKyoB,aAAc,EACnBzoB,KAAK0oB,QAAU1oB,KAAK2oB,MAAO,EAC3B3oB,KAAK4oB,aAEL5oB,KAAKgU,KAAOnH,GAASA,EAAMmH,IAI3B,IAAI6N,GAAO7hB,MAAM,gBAAiB,YAAa,gBAAiB,aAAa6oB,QAAQ,SAAUC,GAC7FjH,EAAKiH,GAAKxlB,EAAKue,EAAKiH,GAAIjH,KA4S5B,QAASkH,IAAS9c,GAChB,GAAI,OAAOvK,KAAKuK,EAAGwD,cAAe,CAGhC,GAAIuZ,GAAO/c,EAAGgd,uBACd,SAASD,EAAKE,OAASF,EAAKG,QAE5B,QAASld,EAAGmd,aAAend,EAAGic,cAAgBjc,EAAGod,iBAAiBtoB,QAwDtE,QAASshB,IAAQpW,EAAIyG,EAAS4W,GAE5B,GAAIC,GAAaD,IAAY5W,EAAQ8W,aAAeC,GAAYxd,EAAIyG,GAAW,KAE3EgX,EAAgBH,GAAcA,EAAWI,UAAcC,GAAS3d,KAAOA,EAAGuE,gBAA4D,KAA1CqZ,GAAgB5d,EAAGiV,WAAYxO,EAe/H,OAAO,UAAyB1R,EAAIiL,EAAI4U,EAAM7E,EAAOjK,GAEnD,GAAImP,GAAatd,EAAQqI,EAAGiV,YAExB4I,EAAOC,GAAe,WACpBR,GAAYA,EAAWvoB,EAAIiL,EAAI4U,EAAM7E,EAAOjK,GAC5C2X,GAAaA,EAAY1oB,EAAIkgB,EAAYL,EAAM7E,EAAOjK,IACzD/Q,EACH,OAAOgpB,IAAahpB,EAAI8oB,IAY5B,QAASC,IAAenJ,EAAQ5f,GAQ5BA,EAAGmW,cAEL,IAAI8S,GAAmBjpB,EAAGmW,YAAYpW,MACtC6f,IACA,IAAIkJ,GAAO9oB,EAAGmW,YAAYzU,MAAMunB,EAChCC,IAAeJ,EACf,KAAK,GAAIhpB,GAAI,EAAG2C,EAAIqmB,EAAK/oB,OAAQD,EAAI2C,EAAG3C,IACtCgpB,EAAKhpB,GAAGqpB,OAEV,OAAOL,GAQT,QAASI,IAAeJ,GACtB,GAAoB,IAAhBA,EAAK/oB,OAAT,CAEA,GACID,GAAGspB,EAAGlF,EAAGzhB,EADT4mB,KAEAtjB,EAAQ,EACRujB,IACJ,KAAKxpB,EAAI,EAAGspB,EAAIN,EAAK/oB,OAAQD,EAAIspB,EAAGtpB,IAAK,CACvC,GAAI8I,GAAMkgB,EAAKhpB,GACXypB,EAAW3gB,EAAI4gB,WAAW/lB,IAAI8lB,UAAYE,GAC1CC,EAAQL,EAAWE,EAClBG,KACHA,EAAQL,EAAWE,MACnBD,EAAW1hB,KAAK2hB,IAElBG,EAAM9hB,KAAKgB,GAMb,IAHA0gB,EAAWK,KAAK,SAAUnoB,EAAGC,GAC3B,MAAOD,GAAIC,GAAI,EAAKD,IAAMC,EAAI,EAAI,IAE/B3B,EAAI,EAAGspB,EAAIE,EAAWvpB,OAAQD,EAAIspB,EAAGtpB,IAAK,CAC7C,GAAI8pB,GAAQP,EAAWC,EAAWxpB,GAClC,KAAKokB,EAAI,EAAGzhB,EAAImnB,EAAM7pB,OAAQmkB,EAAIzhB,EAAGyhB,IACnC4E,EAAK/iB,KAAW6jB,EAAM1F,KAoB5B,QAAS8E,IAAahpB,EAAI8oB,EAAM3kB,EAAS0lB,GACvC,QAAS5J,GAAO6J,GACdC,GAAa/pB,EAAI8oB,EAAMgB,GACnB3lB,GAAW0lB,GACbE,GAAa5lB,EAAS0lB,GAK1B,MADA5J,GAAO6I,KAAOA,EACP7I,EAWT,QAAS8J,IAAa/pB,EAAI8oB,EAAMgB,GAE9B,IADA,GAAIhqB,GAAIgpB,EAAK/oB,OACND,KACLgpB,EAAKhpB,GAAGkqB,YAeZ,QAASC,IAAoBjqB,EAAIiL,EAAI6H,EAAOkI,GAC1C,GAAIkP,GAAc3F,GAAatZ,EAAI6H,EAAO9S,GACtCmqB,EAAWpB,GAAe,WAC5BmB,EAAYlqB,EAAIgb,IACfhb,EACH,OAAOgpB,IAAahpB,EAAImqB,GAkB1B,QAASC,IAAYnf,EAAIyG,EAAS2Y,GAChC,GAEIC,GAAeC,EAFfC,EAAiB9Y,EAAQ+Y,gBACzBC,EAAgBhZ,EAAQiZ,cAwB5B,OAnBoB,MAAhB1f,EAAG2B,WAGD8E,EAAQ8W,cAENgC,GAAkBH,IACpBC,EAAgBM,GAAkBJ,EAAgBH,IAEhDK,IAEFH,EAAiBK,GAAkBF,EAAehZ,KAIpD6Y,EAAiBK,GAAkB3f,EAAGyF,WAAYgB,IAItDA,EAAQ+Y,gBAAkB/Y,EAAQiZ,eAAiB,KAC5C,SAAoB3qB,EAAIiL,EAAI+P,GAEjC,GACI6O,GADA1lB,EAAUnE,EAAGkX,QAEb/S,IAAWmmB,IACbT,EAAcd,GAAe,WAC3BuB,EAAcnmB,EAAS8G,EAAI,KAAM+P,IAChC7W,GAIL,IAAI0mB,GAAW9B,GAAe,WACxBwB,GAAgBA,EAAevqB,EAAIiL,IACtCjL,EAIH,OAAOgpB,IAAahpB,EAAI6qB,EAAU1mB,EAAS0lB,IAa/C,QAASpB,IAAYnc,EAAMoF,GACzB,GAAIsB,GAAO1G,EAAKM,QAChB,OAAa,KAAToG,GAAe4V,GAAStc,GAER,IAAT0G,GAAc1G,EAAKwD,KAAK9H,OAC1B8iB,GAAgBxe,EAAMoF,GAEtB,KAJAqZ,GAAeze,EAAMoF,GAgBhC,QAASqZ,IAAe9f,EAAIyG,GAI1B,GAAmB,aAAfzG,EAAG8E,QAAwB,CAE7B,GAA6B,OAAzBjD,EAAQ7B,EAAI,SACd,MAAO+f,GAET,IAAI7gB,GAASN,EAAUoB,EAAGjK,MACtBmJ,KACFc,EAAGyD,aAAa,SAAUnE,EAAYJ,IACtCc,EAAGjK,MAAQ,IAGf,GAAIiqB,GACAtZ,EAAW1G,EAAGuF,gBACdC,EAAQkB,GAAY/O,EAAQqI,EAAGyF,WAiBnC,OAfIiB,KACFsZ,EAASC,GAAwBjgB,EAAIwF,EAAOiB,IAGzCuZ,IACHA,EAASE,GAAuBlgB,EAAIyG,IAGjCuZ,IACHA,EAASG,GAAengB,EAAIyG,KAGzBuZ,GAAUtZ,IACbsZ,EAASL,GAAkBna,EAAOiB,IAE7BuZ,EAWT,QAASH,IAAgBxe,EAAMoF,GAE7B,GAAIpF,EAAK+e,MACP,MAAOC,GAGT,IAAInhB,GAASN,EAAUyC,EAAKif,UAC5B,KAAKphB,EACH,MAAO,KAST,KADA,GAAInE,GAAOsG,EAAKiB,YACTvH,GAA0B,IAAlBA,EAAK4G,UAClB5G,EAAKqlB,OAAQ,EACbrlB,EAAOA,EAAKuH,WAKd,KAAK,GADDtC,GAAIR,EADJsG,EAAO5E,SAASuD,yBAEX5P,EAAI,EAAG2C,EAAI0H,EAAOpK,OAAQD,EAAI2C,EAAG3C,IACxC2K,EAAQN,EAAOrK,GACfmL,EAAKR,EAAMH,IAAMkhB,GAAiB/gB,EAAOiH,GAAWvF,SAASkE,eAAe5F,EAAMzJ,OAClF+P,EAAK3F,YAAYH,EAEnB,OAAOwgB,IAAmBthB,EAAQ4G,EAAMW,GAU1C,QAAS4Z,IAAWtrB,EAAIsM,GACtBd,GAAOc,GAWT,QAASkf,IAAiB/gB,EAAOiH,GAgB/B,QAASga,GAAa1Y,GACpB,IAAIvI,EAAM+e,WAAV,CACA,GAAIroB,GAASoH,EAAekC,EAAMzJ,MAClCyJ,GAAM+e,YACJzhB,KAAMiL,EACNvP,IAAKkoB,GAAW3Y,GAChBnK,WAAY1H,EAAO0H,WACnBlB,QAASxG,EAAOwG,UAtBpB,GAAIsD,EAyBJ,OAxBIR,GAAMP,QACRe,EAAKkB,SAASkE,eAAe5F,EAAMzJ,OAE/ByJ,EAAMT,MACRiB,EAAKkB,SAASiE,cAAc,UAC5Bsb,EAAa,UAKbzgB,EAAKkB,SAASkE,eAAe,KAC7Bqb,EAAa,SAaVzgB,EAUT,QAASwgB,IAAmBthB,EAAQ4G,GAClC,MAAO,UAAwB/Q,EAAIiL,EAAI4U,EAAM7E,GAI3C,IAAK,GADDvQ,GAAOzJ,EAAOsL,EAFdsf,EAAY7a,EAAKQ,WAAU,GAC3B2O,EAAatd,EAAQgpB,EAAU1L,YAE1BpgB,EAAI,EAAG2C,EAAI0H,EAAOpK,OAAQD,EAAI2C,EAAG3C,IACxC2K,EAAQN,EAAOrK,GACfkB,EAAQyJ,EAAMzJ,MACVyJ,EAAMH,MACRgC,EAAO4T,EAAWpgB,GACd2K,EAAMP,SACRlJ,GAASga,GAAShb,GAAI6K,MAAM7J,GACxByJ,EAAMT,KACRpI,GAAQ0K,EAAMgT,GAActe,GAAO,IAEnCsL,EAAKwD,KAAO/O,EAAUC,IAGxBhB,EAAGylB,SAAShb,EAAM+e,WAAYld,EAAMuT,EAAM7E,GAIhDpZ,IAAQqJ,EAAI2gB,IAYhB,QAAS/C,IAAgBgD,EAAUna,GAGjC,IAAK,GADD6W,GAAYG,EAAapc,EADzBwf,KAEKhsB,EAAI,EAAG2C,EAAIopB,EAAS9rB,OAAQD,EAAI2C,EAAG3C,IAC1CwM,EAAOuf,EAAS/rB,GAChByoB,EAAaE,GAAYnc,EAAMoF,GAC/BgX,EAAgBH,GAAcA,EAAWI,UAA8B,WAAjBrc,EAAKyD,UAAwBzD,EAAKkD,gBAA8D,KAA5CqZ,GAAgBvc,EAAK4T,WAAYxO,GAC3Ioa,EAAQlkB,KAAK2gB,EAAYG,EAE3B,OAAOoD,GAAQ/rB,OAASgsB,GAAgBD,GAAW,KAUrD,QAASC,IAAgBD,GACvB,MAAO,UAAqB9rB,EAAImR,EAAO0O,EAAM7E,EAAOjK,GAElD,IAAK,GADDzE,GAAMic,EAAYyD,EACblsB,EAAI,EAAG4hB,EAAI,EAAGjf,EAAIqpB,EAAQ/rB,OAAQD,EAAI2C,EAAGif,IAAK,CACrDpV,EAAO6E,EAAMuQ,GACb6G,EAAauD,EAAQhsB,KACrBksB,EAAiBF,EAAQhsB,IAEzB,IAAIogB,GAAatd,EAAQ0J,EAAK4T,WAC1BqI,IACFA,EAAWvoB,EAAIsM,EAAMuT,EAAM7E,EAAOjK,GAEhCib,GACFA,EAAehsB,EAAIkgB,EAAYL,EAAM7E,EAAOjK,KAcpD,QAASoa,IAAuBlgB,EAAIyG,GAClC,GAAIpH,GAAMW,EAAG8E,QAAQ5N,aACrB,KAAIyP,GAAYlR,KAAK4J,GAArB,CAGA,GAAI7G,GAAMsO,GAAaL,EAAS,oBAAqBpH,EACrD,OAAI7G,GACKwoB,GAAuBhhB,EAAIX,EAAK,GAAIoH,EAASjO,GADtD,QAcF,QAAS2nB,IAAengB,EAAIyG,GAC1B,GAAIwa,GAAYza,GAAmBxG,EAAIyG,EACvC,IAAIwa,EAAW,CACb,GAAIC,GAAM5b,GAAQtF,GACdue,GACFzhB,KAAM,YACNokB,IAAKA,EACLtjB,WAAYqjB,EAAUla,GACtBvO,IAAK2oB,GAAmBF,UACxBG,WACEC,SAAUJ,EAAU7jB,UAGpBkkB,EAAkB,SAAyBvsB,EAAIiL,EAAI4U,EAAM7E,EAAOjK,GAC9Dob,GACFjX,IAAgB8F,GAAShb,GAAIgW,MAAOmW,EAAK,MAE3CnsB,EAAGylB,SAAS+D,EAAYve,EAAI4U,EAAM7E,EAAOjK,GAG3C,OADAwb,GAAgB5D,UAAW,EACpB4D,GAcX,QAASrB,IAAwBjgB,EAAIwF,EAAOiB,GAE1C,GAA6B,OAAzB5E,EAAQ7B,EAAI,SACd,MAAO+f,GAGT,IAAI/f,EAAGmC,aAAa,UAAW,CAC7B,GAAIof,GAAOvhB,EAAGwhB,sBACd,IAAID,GAAQA,EAAKpf,aAAa,QAC5B,MAAO4d,IAKX,IAAK,GADDvG,GAAM1c,EAAM/G,EAAOqrB,EAAWK,EAASC,EAASC,EAAS1kB,EAAKzE,EAAKopB,EAC9D/sB,EAAI,EAAGspB,EAAI3Y,EAAM1Q,OAAQD,EAAIspB,EAAGtpB,IACvC2kB,EAAOhU,EAAM3Q,GACbiI,EAAO0c,EAAK1c,KAAKnG,QAAQkrB,GAAY,KACjCJ,EAAU3kB,EAAKgC,MAAMgjB,OACvBtpB,EAAMsO,GAAaL,EAAS,aAAcgb,EAAQ,IAC9CjpB,GAAOA,EAAIklB,YACRkE,IAAYppB,EAAI8lB,UAAYyD,IAA6BH,EAAQtD,YACpEsD,EAAUppB,EACVmpB,EAAUnI,EAAK1c,KACfskB,EAAYY,GAAexI,EAAK1c,MAChC/G,EAAQyjB,EAAKzjB,MACb2rB,EAAUD,EAAQ,GAClBxkB,EAAMwkB,EAAQ,IAMtB,OAAIG,GACKZ,GAAuBhhB,EAAI0hB,EAAS3rB,EAAO0Q,EAASmb,EAASD,EAAS1kB,EAAKmkB,GADpF,OAKF,QAASrB,OAoBT,QAASiB,IAAuBhhB,EAAI0hB,EAAS3rB,EAAO0Q,EAASjO,EAAKmpB,EAAS1kB,EAAKmkB,GAC9E,GAAIlrB,GAASoH,EAAevH,GACxBwoB,GACFzhB,KAAM4kB,EACNzkB,IAAKA,EACLW,WAAY1H,EAAO0H,WACnBlB,QAASxG,EAAOwG,QAChB2R,IAAKtY,EACLyjB,KAAMmI,EACNP,UAAWA,EACX5oB,IAAKA,EAGS,SAAZkpB,GAAiC,gBAAZA,IACvBnD,EAAW2C,IAAM5b,GAAQtF,GAE3B,IAAI1I,GAAK,SAA4BvC,EAAIiL,EAAI4U,EAAM7E,EAAOjK,GACpDyY,EAAW2C,KACbjX,IAAgB8F,GAAShb,GAAIgW,MAAOwT,EAAW2C,IAAK,MAEtDnsB,EAAGylB,SAAS+D,EAAYve,EAAI4U,EAAM7E,EAAOjK,GAG3C,OADAxO,GAAGomB,UAAW,EACPpmB,EAWT,QAASqoB,IAAkBna,EAAOiB,GAwEhC,QAASwb,GAAQP,EAASlpB,EAAK0pB,GAC7B,GAAIC,GAAkBD,GAAgBE,GAAWF,GAC7ChsB,GAAUisB,GAAmB7kB,EAAevH,EAChD8nB,GAAKlhB,MACHG,KAAM4kB,EACNlI,KAAMmI,EACNtT,IAAKuM,EACLpiB,IAAKA,EACLyE,IAAKA,EACLmkB,UAAWA,EAIXxjB,WAAY1H,GAAUA,EAAO0H,WAC7BlB,QAASxG,GAAUA,EAAOwG,QAC1B2lB,OAAQH,EACRE,WAAYD,IApFhB,IAHA,GAEI3I,GAAM1c,EAAM/G,EAAO4rB,EAAS/G,EAAU8G,EAASzkB,EAAKmkB,EAAWkB,EAAQpjB,EAAQuiB,EAF/E5sB,EAAI2Q,EAAM1Q,OACV+oB,KAEGhpB,KAYL,GAXA2kB,EAAOhU,EAAM3Q,GACbiI,EAAO6kB,EAAUnI,EAAK1c,KACtB/G,EAAQ6kB,EAAWpB,EAAKzjB,MACxBmJ,EAASN,EAAU7I,GAEnBkH,EAAM,KAENmkB,EAAYY,GAAellB,GAC3BA,EAAOA,EAAKnG,QAAQkrB,GAAY,IAG5B3iB,EACFnJ,EAAQuJ,EAAYJ,GACpBjC,EAAMH,EACNmlB,EAAQ,OAAQvB,GAAWrpB,KAAM6H,OAMjC,IAAIqjB,GAAa9sB,KAAKqH,GACpBskB,EAAUC,SAAWmB,GAAO/sB,KAAKqH,GACjCmlB,EAAQ,aAAcd,GAAmBzgB,gBAIzC,IAAI+hB,GAAKhtB,KAAKqH,GACZG,EAAMH,EAAKnG,QAAQ8rB,GAAM,IACzBR,EAAQ,KAAMvB,GAAW/d,QAIzB,IAAI6f,GAAO/sB,KAAKqH,GACd4kB,EAAU5kB,EAAKnG,QAAQ6rB,GAAQ,IACf,UAAZd,GAAmC,UAAZA,EACzBO,EAAQP,EAASP,GAAmBO,KAEpCzkB,EAAMykB,EACNO,EAAQ,OAAQvB,GAAWrpB,WAK7B,IAAIoqB,EAAU3kB,EAAKgC,MAAMgjB,IAAY,CAKnC,GAJAJ,EAAUD,EAAQ,GAClBxkB,EAAMwkB,EAAQ,GAGE,SAAZC,EACF,QAGFY,GAASxb,GAAaL,EAAS,aAAcib,GAAS,GAClDY,GACFL,EAAQP,EAASY,GAiC/B,GAAIzE,EAAK/oB,OACP,MAAO4tB,IAAe7E,GAW1B,QAASmE,IAAellB,GACtB,GAAIyK,GAAMnP,OAAOwC,OAAO,MACpBkE,EAAQhC,EAAKgC,MAAM+iB,GACvB,IAAI/iB,EAEF,IADA,GAAIjK,GAAIiK,EAAMhK,OACPD,KACL0S,EAAIzI,EAAMjK,GAAG4B,MAAM,KAAM,CAG7B,OAAO8Q,GAUT,QAASmb,IAAehC,GACtB,MAAO,UAAoB3rB,EAAIiL,EAAI4U,EAAM7E,EAAOjK,GAG9C,IADA,GAAIjR,GAAI6rB,EAAW5rB,OACZD,KACLE,EAAGylB,SAASkG,EAAW7rB,GAAImL,EAAI4U,EAAM7E,EAAOjK,IAYlD,QAASsc,IAAWljB,GAElB,IADA,GAAIrK,GAAIqK,EAAOpK,OACRD,KACL,GAAIqK,EAAOrK,GAAGoK,QAAS,OAAO,EAIlC,QAAS0e,IAAS3d,GAChB,MAAsB,WAAfA,EAAG8E,WAA0B9E,EAAGmC,aAAa,SAAuC,oBAA5BnC,EAAG+B,aAAa,SAiBjF,QAAS4gB,IAAW3iB,EAAIyG,GA8BtB,MAxBIA,KACFA,EAAQ+Y,gBAAkBoD,GAAa5iB,IAIrCoE,GAAWpE,KACbA,EAAKqU,GAAcrU,IAEjByG,IACEA,EAAQ8W,eAAiB9W,EAAQ6N,WACnC7N,EAAQ6N,SAAW,iBAEjB7N,EAAQ6N,WACV7N,EAAQoc,SAAW7e,GAAehE,GAClCA,EAAK8iB,GAAmB9iB,EAAIyG,KAG5BpC,GAAWrE,KAIbwC,GAAQuC,GAAa,WAAW,GAAO/E,GACvCA,EAAGG,YAAY4E,GAAa,SAAS,KAEhC/E,EAYT,QAAS8iB,IAAmB9iB,EAAIyG,GAC9B,GAAI6N,GAAW7N,EAAQ6N,SACnBxO,EAAOuO,GAAcC,GAAU,EACnC,IAAIxO,EAAM,CACR,GAAIid,GAAWjd,EAAKrD,UACpB,KAAKsgB,EACH,MAAOjd,EAET,IAAIzG,GAAM0jB,EAASje,SAAWie,EAASje,QAAQ5N,aAC/C,OAAIuP,GAAQ9P,SAENqJ,IAAOkB,SAASsO,KAQpB1J,EAAKmP,WAAWngB,OAAS,GAEH,IAAtBiuB,EAASphB,UAED,cAARtC,GAAuByH,GAAaL,EAAS,aAAcpH,IAAQ6C,GAAY6gB,EAAU,OAEzFjc,GAAaL,EAAS,oBAAqBpH,IAE3C0jB,EAAS5gB,aAAa,UAEtB4gB,EAAS5gB,aAAa,QACb2D,GAEPW,EAAQiZ,eAAiBkD,GAAaG,GACtCC,GAAWhjB,EAAI+iB,GACRA,KAGT/iB,EAAGG,YAAY2F,GACR9F,IAeb,QAAS4iB,IAAa5iB,GACpB,GAAoB,IAAhBA,EAAG2B,UAAkB3B,EAAGuF,gBAC1B,MAAO5N,GAAQqI,EAAGyF,YAYtB,QAASud,IAAW9qB,EAAMD,GAIxB,IAHA,GAEI6E,GAAM/G,EAFNyP,EAAQtN,EAAKuN,WACb5Q,EAAI2Q,EAAM1Q,OAEPD,KACLiI,EAAO0I,EAAM3Q,GAAGiI,KAChB/G,EAAQyP,EAAM3Q,GAAGkB,MACZkC,EAAGkK,aAAarF,IAAUmmB,GAAcxtB,KAAKqH,GAE9B,UAATA,IAAqB8B,EAAU7I,KAAWA,EAAQA,EAAMgH,SACjEhH,EAAMmjB,MAAM,OAAO0D,QAAQ,SAAUtZ,GACnCI,GAASzL,EAAIqL,KAHfrL,EAAGwL,aAAa3G,EAAM/G,GAoB5B,QAASmtB,IAAanuB,EAAIuP,GACxB,GAAKA,EAAL,CAKA,IAAK,GADDtE,GAAIlD,EADJqmB,EAAWpuB,EAAGquB,cAAgBhrB,OAAOwC,OAAO,MAEvC/F,EAAI,EAAG2C,EAAI8M,EAAQ8H,SAAStX,OAAQD,EAAI2C,EAAG3C,IAClDmL,EAAKsE,EAAQ8H,SAASvX,IAElBiI,EAAOkD,EAAG+B,aAAa,WACxBohB,EAASrmB,KAAUqmB,EAASrmB,QAAaH,KAAKqD,EAKnD,KAAKlD,IAAQqmB,GACXA,EAASrmB,GAAQumB,GAAgBF,EAASrmB,GAAOwH,EAEnD,IAAIA,EAAQC,gBAAiB,CAC3B,GAAI2B,GAAQ5B,EAAQ2Q,UACpB,IAAqB,IAAjB/O,EAAMpR,QAAsC,IAAtBoR,EAAM,GAAGvE,WAAmBuE,EAAM,GAAGrB,KAAK9H,OAClE,MAEFomB,GAAkB,QAAIE,GAAgB/e,EAAQ2Q,WAAY3Q,KAW9D,QAAS+e,IAAgBnd,EAAOzE,GAC9B,GAAIqE,GAAO5E,SAASuD,wBACpByB,GAAQvO,EAAQuO,EAChB,KAAK,GAAIrR,GAAI,EAAG2C,EAAI0O,EAAMpR,OAAQD,EAAI2C,EAAG3C,IAAK,CAC5C,GAAIwM,GAAO6E,EAAMrR,IACbuP,GAAW/C,IAAUA,EAAKc,aAAa,SAAYd,EAAKc,aAAa,WACvEV,EAAOc,YAAYlB,GACnBA,EAAOgT,GAAchT,GAAM,IAE7ByE,EAAK3F,YAAYkB,GAEnB,MAAOyE,GAaT,QAASwd,IAAYxvB,GA4KnB,QAAS+a,MAsBT,QAAS0U,GAAmBnZ,EAAQoZ,GAClC,GAAIxS,GAAU,GAAIM,IAAQkS,EAAOpZ,EAAQ,MACvCwH,MAAM,GAER,OAAO,YAOL,MANIZ,GAAQW,OACVX,EAAQyS,WAEN3a,GAAI7I,QACN+Q,EAAQzG,SAEHyG,EAAQjb,OAtMnBqC,OAAOM,eAAe5E,EAAI4U,UAAW,SACnChL,IAAK,WACH,MAAO3J,MAAKO,OAEdN,IAAK,SAAa0vB,GACZA,IAAY3vB,KAAKO,OACnBP,KAAK4vB,SAASD,MAapB5vB,EAAI4U,UAAU+D,WAAa,WACzB1Y,KAAK6vB,aACL7vB,KAAK8vB,YACL9vB,KAAK+vB,eACL/vB,KAAKgwB,YACLhwB,KAAKiwB,iBAOPlwB,EAAI4U,UAAUkb,WAAa,WACzB,GAAInd,GAAU1S,KAAKsY,SACfrM,EAAKyG,EAAQzG,GACb6H,EAAQpB,EAAQoB,KAKpB7H,GAAKyG,EAAQzG,GAAKiB,EAAMjB,GACxBjM,KAAKkwB,eAAiBjkB,GAAsB,IAAhBA,EAAG2B,UAAkBkG,EAE/CmX,GAAoBjrB,KAAMiM,EAAI6H,EAAO9T,KAAKmY,QAAU,MAOxDpY,EAAI4U,UAAUqb,UAAY,WACxB,GAAIG,GAASnwB,KAAKsY,SAASxH,KACvBA,EAAO9Q,KAAKO,MAAQ4vB,EAASA,MAC5B5rB,GAAcuM,KACjBA,KAGF,IAGIhQ,GAAGX,EAHH2T,EAAQ9T,KAAKqmB,OAEbjiB,EAAOC,OAAOD,KAAK0M,EAGvB,KADAhQ,EAAIsD,EAAKrD,OACFD,KACLX,EAAMiE,EAAKtD,GAKNgT,GAAUzT,EAAOyT,EAAO3T,IAC3BH,KAAKiB,OAAOd,EAIhB4V,IAAQjF,EAAM9Q,OAShBD,EAAI4U,UAAUib,SAAW,SAAUD,GACjCA,EAAUA,KACV,IAAIS,GAAUpwB,KAAKO,KACnBP,MAAKO,MAAQovB,CACb,IAAIvrB,GAAMjE,EAAKW,CAIf,KAFAsD,EAAOC,OAAOD,KAAKgsB,GACnBtvB,EAAIsD,EAAKrD,OACFD,KACLX,EAAMiE,EAAKtD,GACLX,IAAOwvB,IACX3vB,KAAKoB,SAASjB,EAOlB,KAFAiE,EAAOC,OAAOD,KAAKurB,GACnB7uB,EAAIsD,EAAKrD,OACFD,KACLX,EAAMiE,EAAKtD,GACNT,EAAOL,KAAMG,IAEhBH,KAAKiB,OAAOd,EAGhBiwB,GAAQ3vB,OAAO4vB,SAASrwB,MACxB+V,GAAQ4Z,EAAS3vB,MACjBA,KAAKkB,WAUPnB,EAAI4U,UAAU1T,OAAS,SAAUd,GAC/B,IAAKwB,EAAWxB,GAAM,CAKpB,GAAI0hB,GAAO7hB,IACXqE,QAAOM,eAAekd,EAAM1hB,GAC1B0E,cAAc,EACdH,YAAY,EACZiF,IAAK,WACH,MAAOkY,GAAKthB,MAAMJ,IAEpBF,IAAK,SAAqBG,GACxByhB,EAAKthB,MAAMJ,GAAOC,OAY1BL,EAAI4U,UAAUvT,SAAW,SAAUjB,GAC5BwB,EAAWxB,UACPH,MAAKG,IAQhBJ,EAAI4U,UAAUzT,QAAU,WACtB,IAAK,GAAIJ,GAAI,EAAG2C,EAAIzD,KAAKkX,UAAUnW,OAAQD,EAAI2C,EAAG3C,IAChDd,KAAKkX,UAAUpW,GAAGwvB,QAAO,IAU7BvwB,EAAI4U,UAAUsb,cAAgB,WAC5B,GAAIM,GAAWvwB,KAAKsY,SAASiY,QAC7B,IAAIA,EACF,IAAK,GAAIpwB,KAAOowB,GAAU,CACxB,GAAIC,GAAUD,EAASpwB,GACnBsE,GACFC,YAAY,EACZG,cAAc,EAEO,mBAAZ2rB,IACT/rB,EAAIkF,IAAM6lB,EAAmBgB,EAASxwB,MACtCyE,EAAIxE,IAAM6a,IAEVrW,EAAIkF,IAAM6mB,EAAQ7mB,IAAM6mB,EAAQ5lB,SAAU,EAAQ4kB,EAAmBgB,EAAQ7mB,IAAK3J,MAAQsD,EAAKktB,EAAQ7mB,IAAK3J,MAAQ8a,EACpHrW,EAAIxE,IAAMuwB,EAAQvwB,IAAMqD,EAAKktB,EAAQvwB,IAAKD,MAAQ8a,GAEpDzW,OAAOM,eAAe3E,KAAMG,EAAKsE,KA0BvC1E,EAAI4U,UAAUob,aAAe,WAC3B,GAAIU,GAAUzwB,KAAKsY,SAASmY,OAC5B,IAAIA,EACF,IAAK,GAAItwB,KAAOswB,GACdzwB,KAAKG,GAAOmD,EAAKmtB,EAAQtwB,GAAMH,OASrCD,EAAI4U,UAAUmb,UAAY,WACxB,GAAIY,GAAQ1wB,KAAKsY,SAASqY,KAC1B,IAAID,EACF,IAAK,GAAIvwB,KAAOuwB,GACdxa,GAAelW,KAAMG,EAAKuwB,EAAMvwB,KAQxC,QAASywB,IAAa7wB,GAuBpB,QAAS8wB,GAAwB7vB,EAAIiL,GAGnC,IAAK,GADDlD,GAAM/G,EAAOwhB,EADb/R,EAAQxF,EAAGyF,WAEN5Q,EAAI,EAAG2C,EAAIgO,EAAM1Q,OAAQD,EAAI2C,EAAG3C,IACvCiI,EAAO0I,EAAM3Q,GAAGiI,KACZ+nB,GAAQpvB,KAAKqH,KACfA,EAAOA,EAAKnG,QAAQkuB,GAAS,IAI7B9uB,EAAQyP,EAAM3Q,GAAGkB,MACbma,GAAana,KACfA,GAAS,4BAEXwhB,GAAWxiB,EAAGmX,QAAUnX,EAAGkX,UAAUrM,MAAM7J,GAAO,GAClDwhB,EAAQuN,aAAc,EACtB/vB,EAAGgwB,IAAIjoB,EAAKnG,QAAQkuB,IAAUtN,IAapC,QAASyN,GAAkBjwB,EAAIiM,EAAQikB,GACrC,GAAKA,EAAL,CACA,GAAIC,GAAUhxB,EAAKW,EAAGspB,CACtB,KAAKjqB,IAAO+wB,GAEV,GADAC,EAAWD,EAAK/wB,GACZ4T,GAAQod,GACV,IAAKrwB,EAAI,EAAGspB,EAAI+G,EAASpwB,OAAQD,EAAIspB,EAAGtpB,IACtCswB,EAASpwB,EAAIiM,EAAQ9M,EAAKgxB,EAASrwB,QAGrCswB,GAASpwB,EAAIiM,EAAQ9M,EAAKgxB,IAehC,QAASC,GAASpwB,EAAIiM,EAAQ9M,EAAKqjB,EAAS9Q,GAC1C,GAAIsB,SAAcwP,EAClB,IAAa,aAATxP,EACFhT,EAAGiM,GAAQ9M,EAAKqjB,EAAS9Q,OACpB,IAAa,WAATsB,EAAmB,CAC5B,GAAIyc,GAAUzvB,EAAGsX,SAASmY,QACtBhP,EAASgP,GAAWA,EAAQjN,EAC5B/B,IACFzgB,EAAGiM,GAAQ9M,EAAKshB,EAAQ/O,OAIjB8Q,IAAoB,WAATxP,GACpBod,EAASpwB,EAAIiM,EAAQ9M,EAAKqjB,EAAQA,QAASA,GAiB/C,QAAS6N,KACFrxB,KAAK8X,cACR9X,KAAK8X,aAAc,EACnB9X,KAAK+W,UAAU8R,QAAQyI,IAU3B,QAASA,GAAWnhB,IACbA,EAAM2H,aAAezK,EAAM8C,EAAM0G,MACpC1G,EAAMsI,UAAU,YAQpB,QAAS8Y,KACHvxB,KAAK8X,cACP9X,KAAK8X,aAAc,EACnB9X,KAAK+W,UAAU8R,QAAQ2I,IAU3B,QAASA,GAAWrhB,GACdA,EAAM2H,cAAgBzK,EAAM8C,EAAM0G,MACpC1G,EAAMsI,UAAU,YA1IpB1Y,EAAI4U,UAAUgE,YAAc,WAC1B,GAAIjG,GAAU1S,KAAKsY,QACf5F,GAAQ8W,cACVqH,EAAwB7wB,KAAM0S,EAAQzG,IAExCglB,EAAkBjxB,KAAM,MAAO0S,EAAQ+e,QACvCR,EAAkBjxB,KAAM,SAAU0S,EAAQgf,QAqF5C3xB,EAAI4U,UAAUgd,cAAgB,WAC5B3xB,KAAKgxB,IAAI,gBAAiBK,GAC1BrxB,KAAKgxB,IAAI,gBAAiBO,IAuD5BxxB,EAAI4U,UAAU8D,UAAY,SAAUmZ,GAClC5xB,KAAK6xB,MAAM,YAAcD,EACzB,IAAIT,GAAWnxB,KAAKsY,SAASsZ,EAC7B,IAAIT,EACF,IAAK,GAAIrwB,GAAI,EAAGspB,EAAI+G,EAASpwB,OAAQD,EAAIspB,EAAGtpB,IAC1CqwB,EAASrwB,GAAGQ,KAAKtB,KAGrBA,MAAK6xB,MAAM,QAAUD,IAIzB,QAASE,OA4BT,QAASC,IAAUvH,EAAYxpB,EAAIiL,EAAI4U,EAAM7E,EAAOjK,GAClD/R,KAAKgB,GAAKA,EACVhB,KAAKiM,GAAKA,EAEVjM,KAAKwqB,WAAaA,EAClBxqB,KAAK+I,KAAOyhB,EAAWzhB,KACvB/I,KAAK6J,WAAa2gB,EAAW3gB,WAC7B7J,KAAKkJ,IAAMshB,EAAWthB,IACtBlJ,KAAKqtB,UAAY7C,EAAW6C,UAC5BrtB,KAAK2I,QAAU6hB,EAAW7hB,QAC1B3I,KAAKstB,QAAUttB,KAAKqtB,WAAartB,KAAKqtB,UAAUC,QAEhDttB,KAAKgyB,SAAU,EACfhyB,KAAKiyB,QAAS,EACdjyB,KAAKkyB,WAAa,KAElBlyB,KAAKmyB,MAAQtR,EACb7gB,KAAKmY,OAAS6D,EACdhc,KAAKoY,MAAQrG,EAmPf,QAASqgB,IAAgBryB,GAOvBA,EAAI4U,UAAU6D,WAAa,SAAUhM,GACnC,GAAI2gB,GAAMntB,KAAKsY,SAAS+Z,IACxB,IAAIlF,EAAK,CACP,GAAImF,IAAQtyB,KAAKmY,QAAUnY,KAAKkY,UAAUlB,KACtCxK,GACE8lB,EAAKnF,KAASntB,OAChBsyB,EAAKnF,GAAO,MAGdmF,EAAKnF,GAAOntB,OAiBlBD,EAAI4U,UAAU4d,SAAW,SAAUtmB,GACjC,GAAIyG,GAAU1S,KAAKsY,SAOfsC,EAAW3O,CAKf,IAJAA,EAAK2iB,GAAW3iB,EAAIyG,GACpB1S,KAAKwyB,aAAavmB,GAGE,IAAhBA,EAAG2B,UAA2C,OAAzBE,EAAQ7B,EAAI,SAArC,CAMA,GAAIof,GAAiBrrB,KAAKkY,UAAYlY,KAAKkY,SAASI,SAChDma,EAAarH,GAAYnf,EAAIyG,EAAS2Y,EAG1C8D,IAAanvB,KAAM0S,EAAQoc,SAG3B,IAAI4D,GACAC,EAAO3yB,KAAKuY,WAGZ7F,GAAQkgB,kBACVF,EAAgBC,EAAK/R,OAChB8R,IACHA,EAAgBC,EAAK/R,OAASyB,GAAQpW,EAAIyG,IAM9C,IAAImgB,GAAeJ,EAAWzyB,KAAMiM,EAAIjM,KAAKmY,QACzC2a,EAAkBJ,EAAgBA,EAAc1yB,KAAMiM,GAAMoW,GAAQpW,EAAIyG,GAAS1S,KAAMiM;AAI3FjM,KAAKiY,UAAY,WACf4a,IAGAC,GAAgB,IAIdpgB,EAAQ9P,SACVA,GAAQgY,EAAU3O,GAGpBjM,KAAK+M,aAAc,EACnB/M,KAAKyY,UAAU,cAUjB1Y,EAAI4U,UAAU6d,aAAe,SAAUvmB,GACjCqE,GAAWrE,IACbjM,KAAKwX,aAAc,EACnBxX,KAAK6W,IAAM7W,KAAK0X,eAAiBzL,EAAGyC,WACpC1O,KAAK2X,aAAe1L,EAAG4E,UAEc,IAAjC7Q,KAAK0X,eAAe9J,WACtB5N,KAAK0X,eAAe5G,KAAO9Q,KAAK2X,aAAa7G,KAAO,IAEtD9Q,KAAKyX,UAAYxL,GAEjBjM,KAAK6W,IAAM5K,EAEbjM,KAAK6W,IAAImM,QAAUhjB,KACnBA,KAAKyY,UAAU,kBAajB1Y,EAAI4U,UAAU8R,SAAW,SAAU+D,EAAYld,EAAMuT,EAAM7E,EAAOjK,GAChE/R,KAAKmX,YAAYvO,KAAK,GAAImpB,IAAUvH,EAAYxqB,KAAMsN,EAAMuT,EAAM7E,EAAOjK,KAY3EhS,EAAI4U,UAAUoe,SAAW,SAAUvmB,EAAQwmB,GACzC,GAAIhzB,KAAK+X,kBAIP,YAHKib,GACHhzB,KAAKizB,WAKT,IAAIC,GACAC,EAEAtR,EAAO7hB,KAKPozB,EAAoB,YAClBF,GAAiBC,GAAmBH,GACtCnR,EAAKoR,WAKLzmB,IAAUxM,KAAK6W,MACjBsc,GAAiB,EACjBnzB,KAAKqzB,QAAQ,WACXF,GAAiB,EACjBC,OAIJpzB,KAAKyY,UAAU,iBACfzY,KAAK+X,mBAAoB,CACzB,IAAIjX,GAGA4M,EAAS1N,KAAKgN,OAQlB,KAPIU,IAAWA,EAAOqK,oBACpBrK,EAAOqJ,UAAUsc,QAAQrzB,MAEzBA,KAAKwY,YAAW,IAGlB1X,EAAId,KAAK+W,UAAUhW,OACZD,KACLd,KAAK+W,UAAUjW,GAAGwyB,UAYpB,KATItzB,KAAKkwB,gBACPlwB,KAAKkwB,iBAIHlwB,KAAKiY,WACPjY,KAAKiY,YAEPnX,EAAId,KAAKkX,UAAUnW,OACZD,KACLd,KAAKkX,UAAUpW,GAAGyyB,UAGhBvzB,MAAK6W,MACP7W,KAAK6W,IAAImM,QAAU,MAGrBkQ,GAAe,EACfE,KASFrzB,EAAI4U,UAAUse,SAAW,WACnBjzB,KAAK4X,eAML5X,KAAKoY,OACPpY,KAAKoY,MAAMC,SAASgb,QAAQrzB,MAI1BA,KAAKO,OAASP,KAAKO,MAAME,QAC3BT,KAAKO,MAAME,OAAO4vB,SAASrwB,MAU7BA,KAAK6W,IAAM7W,KAAKgN,QAAUhN,KAAK8W,MAAQ9W,KAAK+W,UAAY/W,KAAKkX,UAAYlX,KAAKkY,SAAWlY,KAAKmY,OAASnY,KAAKmX,YAAc,KAE1HnX,KAAK4X,cAAe,EACpB5X,KAAKyY,UAAU,aAEfzY,KAAKwzB,SAIT,QAASC,IAAW1zB,GAclBA,EAAI4U,UAAU+e,cAAgB,SAAU1xB,EAAO2xB,EAAUhrB,EAASirB,GAChE,GAAI9qB,GAAQvF,EAAI2B,EAAMgE,EAAK2qB,EAAQ/yB,EAAG2C,EAAG2mB,EAAGlF,CAC5C,KAAKpkB,EAAI,EAAG2C,EAAIkF,EAAQ5H,OAAQD,EAAI2C,EAAG3C,IAGrC,GAFAgI,EAASH,EAAQirB,EAAQnwB,EAAI3C,EAAI,EAAIA,GACrCyC,EAAKwP,GAAa/S,KAAKsY,SAAU,UAAWxP,EAAOC,MAAM,GACpDxF,IACLA,EAAKqwB,EAAQrwB,EAAGqwB,MAAQrwB,EAAGuwB,MAAQvwB,EACjB,kBAAPA,IAAX,CAGA,GAFA2B,EAAO0uB,GAAS5xB,EAAO2xB,IAAa3xB,GACpC6xB,EAASD,EAAQ,EAAI,EACjB9qB,EAAO5D,KACT,IAAKklB,EAAI,EAAGlF,EAAIpc,EAAO5D,KAAKnE,OAAQqpB,EAAIlF,EAAGkF,IACzClhB,EAAMJ,EAAO5D,KAAKklB,GAClBllB,EAAKklB,EAAIyJ,GAAU3qB,EAAIG,QAAUrJ,KAAKwmB,KAAKtd,EAAIlH,OAASkH,EAAIlH,KAGhEA,GAAQuB,EAAGI,MAAM3D,KAAMkF,GAEzB,MAAOlD,IAcTjC,EAAI4U,UAAUof,kBAAoB,SAAU/xB,EAAO8D,GACjD,GAAIpG,EAOJ,IALEA,EADmB,kBAAVsC,GACCA,EAEA+Q,GAAa/S,KAAKsY,SAAU,aAActW,GAAO,GAO7D,GAAKtC,EAAQgT,QA0BX5M,EAAGpG,OAzBH,IAAIA,EAAQs0B,SAEVluB,EAAGpG,EAAQs0B,cACN,IAAIt0B,EAAQu0B,UAEjBv0B,EAAQw0B,iBAAiBtrB,KAAK9C,OACzB,CACLpG,EAAQu0B,WAAY,CACpB,IAAIE,GAAMz0B,EAAQw0B,kBAAoBpuB,EACtCpG,GAAQ4B,KAAKtB,KAAM,SAAiBwT,GAC9BjP,EAAciP,KAChBA,EAAMzT,EAAIkE,OAAOuP,IAGnB9T,EAAQs0B,SAAWxgB,CAEnB,KAAK,GAAI1S,GAAI,EAAG2C,EAAI0wB,EAAIpzB,OAAQD,EAAI2C,EAAG3C,IACrCqzB,EAAIrzB,GAAG0S,IAER,SAAgB4gB,QAa3B,QAASC,IAASt0B,GAyKhB,QAASu0B,GAAMp0B,GACb,MAAOgG,MAAKiT,MAAMjT,KAAKC,UAAUjG,IAjKnCH,EAAI4U,UAAU6R,KAAO,SAAUhlB,EAAK+yB,GAClC,GAAI/gB,GAAMkH,GAAkBlZ,EAC5B,IAAIgS,EAAK,CACP,GAAI+gB,EAAa,CACf,GAAI1S,GAAO7hB,IACX,OAAO,YACL6hB,EAAK2S,WAAa5wB,EAAQF,UAC1B,IAAI2B,GAASmO,EAAI7J,IAAIrI,KAAKugB,EAAMA,EAEhC,OADAA,GAAK2S,WAAa,KACXnvB,GAGT,IACE,MAAOmO,GAAI7J,IAAIrI,KAAKtB,KAAMA,MAC1B,MAAOyW,OAcf1W,EAAI4U,UAAUkG,KAAO,SAAUrZ,EAAKpB,GAClC,GAAIoT,GAAMkH,GAAkBlZ,GAAK,EAC7BgS,IAAOA,EAAIvT,KACbuT,EAAIvT,IAAIqB,KAAKtB,KAAMA,KAAMI,IAU7BL,EAAI4U,UAAU8f,QAAU,SAAUt0B,GAChCgB,EAAInB,KAAKO,MAAOJ,IAelBJ,EAAI4U,UAAU+f,OAAS,SAAUlX,EAAS1X,EAAI4M,GAC5C,GACIvQ,GADAnB,EAAKhB,IAEc,iBAAZwd,KACTrb,EAASoH,EAAeiU,GACxBA,EAAUrb,EAAO0H,WAEnB,IAAIoT,GAAU,GAAIM,IAAQvc,EAAIwc,EAAS1X,GACrC6uB,KAAMjiB,GAAWA,EAAQiiB,KACzBC,KAAMliB,GAAWA,EAAQkiB,KACzBjsB,QAASxG,GAAUA,EAAOwG,QAC1B0U,MAAO3K,GAAWA,EAAQ2K,QAAS,GAKrC,OAHI3K,IAAWA,EAAQmiB,WACrB/uB,EAAGxE,KAAKN,EAAIic,EAAQjb,OAEf,WACLib,EAAQsW,aAYZxzB,EAAI4U,UAAU9I,MAAQ,SAAUf,EAAMypB,GAEpC,GAAIO,GAAWpzB,KAAKoJ,GAAO,CACzB,GAAIlB,GAAML,EAAeuB,GAIrB1K,EAAMJ,KAAKwmB,KAAK5c,EAAIC,WAAY0qB,EACpC,OAAO3qB,GAAIjB,QAAU3I,KAAK0zB,cAActzB,EAAK,KAAMwJ,EAAIjB,SAAWvI,EAGlE,MAAOJ,MAAKwmB,KAAK1b,EAAMypB,IAW3Bx0B,EAAI4U,UAAUogB,aAAe,SAAUjqB,GACrC,GAAIK,GAASN,EAAUC,GACnB9J,EAAKhB,IACT,OAAImL,GACoB,IAAlBA,EAAOpK,OACFC,EAAG6K,MAAMV,EAAO,GAAGnJ,OAAS,GAE5BmJ,EAAOK,IAAI,SAAUC,GAC1B,MAAOA,GAAMH,IAAMtK,EAAG6K,MAAMJ,EAAMzJ,OAASyJ,EAAMzJ,QAChD2J,KAAK,IAGHb,GAYX/K,EAAI4U,UAAUqgB,KAAO,SAAU/b,GAC7B,GAAInI,GAAOmI,EAAOwB,GAAQza,KAAKO,MAAO0Y,GAAQjZ,KAAKO,KAKnD,IAJIuQ,IACFA,EAAOwjB,EAAMxjB,KAGVmI,EAAM,CACT,GAAI9Y,EACJ,KAAKA,IAAOH,MAAKsY,SAASiY,SACxBzf,EAAK3Q,GAAOm0B,EAAMt0B,KAAKG,GAEzB,IAAIH,KAAKqmB,OACP,IAAKlmB,IAAOH,MAAKqmB,OACfvV,EAAK3Q,GAAOm0B,EAAMt0B,KAAKG,IAI7B80B,QAAQC,IAAIpkB,IAgBhB,QAASqkB,IAAQp1B,GAkHf,QAASq1B,GAAOp0B,EAAIkL,EAAQpG,EAAI0b,EAAgB6T,EAAKC,GACnDppB,EAASgB,EAAMhB,EACf,IAAIqpB,IAAoBloB,EAAMnB,GAC1BQ,EAAK8U,KAAmB,GAAS+T,EAAmBF,EAAMC,EAC1DE,GAAkBD,IAAqBv0B,EAAG8W,cAAgBzK,EAAMrM,EAAG6V,IAYvE,OAXI7V,GAAGwW,aACL5F,GAAa5Q,EAAG0W,eAAgB1W,EAAG2W,aAAc,SAAUrK,GACzDZ,EAAGY,EAAMpB,EAAQlL,KAEnB8E,GAAMA,KAEN4G,EAAG1L,EAAG6V,IAAK3K,EAAQlL,EAAI8E,GAErB0vB,GACFx0B,EAAGyX,UAAU,YAERzX,EAST,QAASkM,GAAMjB,GACb,MAAqB,gBAAPA,GAAkBkB,SAASC,cAAcnB,GAAMA,EAY/D,QAASwpB,GAAOxpB,EAAIC,EAAQlL,EAAI8E,GAC9BoG,EAAOE,YAAYH,GACfnG,GAAIA,IAYV,QAAS4vB,GAAazpB,EAAIC,EAAQlL,EAAI8E,GACpCwG,GAAOL,EAAIC,GACPpG,GAAIA,IAWV,QAAS6vB,GAAa1pB,EAAIjL,EAAI8E,GAC5B0G,GAAOP,GACHnG,GAAIA,IA5KV/F,EAAI4U,UAAUihB,UAAY,SAAUryB,GAClC+Z,GAAS/Z,EAAIvD,OAWfD,EAAI4U,UAAUkhB,UAAY,SAAU3pB,EAAQpG,EAAI0b,GAC9C,MAAO4T,GAAOp1B,KAAMkM,EAAQpG,EAAI0b,EAAgBiU,EAAQzpB,IAW1DjM,EAAI4U,UAAUmhB,WAAa,SAAU5pB,EAAQpG,EAAI0b,GAO/C,MANAtV,GAASgB,EAAMhB,GACXA,EAAOsE,gBACTxQ,KAAK+1B,QAAQ7pB,EAAOwC,WAAY5I,EAAI0b,GAEpCxhB,KAAK61B,UAAU3pB,EAAQpG,EAAI0b,GAEtBxhB,MAWTD,EAAI4U,UAAUohB,QAAU,SAAU7pB,EAAQpG,EAAI0b,GAC5C,MAAO4T,GAAOp1B,KAAMkM,EAAQpG,EAAI0b,EAAgBkU,EAAcrpB,IAWhEtM,EAAI4U,UAAUqhB,OAAS,SAAU9pB,EAAQpG,EAAI0b,GAO3C,MANAtV,GAASgB,EAAMhB,GACXA,EAAOqC,YACTvO,KAAK+1B,QAAQ7pB,EAAOqC,YAAazI,EAAI0b,GAErCxhB,KAAK61B,UAAU3pB,EAAOyB,WAAY7H,EAAI0b,GAEjCxhB,MAUTD,EAAI4U,UAAU0e,QAAU,SAAUvtB,EAAI0b,GACpC,IAAKxhB,KAAK6W,IAAIlJ,WACZ,MAAO7H,IAAMA,GAEf,IAAImwB,GAAaj2B,KAAK8X,aAAezK,EAAMrN,KAAK6W,IAG3Cof,KAAYzU,GAAiB,EAClC,IAAIK,GAAO7hB,KACPk2B,EAAS,WACPD,GAAYpU,EAAKpJ,UAAU,YAC3B3S,GAAIA,IAEV,IAAI9F,KAAKwX,YACP1F,GAAgB9R,KAAK0X,eAAgB1X,KAAK2X,aAAc3X,KAAMA,KAAKyX,UAAWye,OACzE,CACL,GAAIxpB,GAAK8U,KAAmB,EAAQmU,EAAeppB,CACnDG,GAAG1M,KAAK6W,IAAK7W,KAAMk2B,GAErB,MAAOl2B,OAsFX,QAASm2B,IAAWp2B,GAmLlB,QAASq2B,GAAoBp1B,EAAI6N,EAAOwnB,GACtC,GAAI3oB,GAAS1M,EAAGgM,OAGhB,IAAKU,GAAW2oB,IAASC,EAAO50B,KAAKmN,GACrC,KAAOnB,GACLA,EAAO6J,aAAa1I,IAAUnB,EAAO6J,aAAa1I,IAAU,GAAKwnB,EACjE3oB,EAASA,EAAOV,QAlLpBjN,EAAI4U,UAAUqc,IAAM,SAAUniB,EAAOtL,GAGnC,OAFCvD,KAAKsX,QAAQzI,KAAW7O,KAAKsX,QAAQzI,QAAcjG,KAAKrF,GACzD6yB,EAAoBp2B,KAAM6O,EAAO,GAC1B7O,MAWTD,EAAI4U,UAAU4hB,MAAQ,SAAU1nB,EAAOtL,GAErC,QAASqL,KACPiT,EAAK2R,KAAK3kB,EAAOD,GACjBrL,EAAGI,MAAM3D,KAAM0D,WAHjB,GAAIme,GAAO7hB,IAOX,OAFA4O,GAAGrL,GAAKA,EACRvD,KAAKgxB,IAAIniB,EAAOD,GACT5O,MAWTD,EAAI4U,UAAU6e,KAAO,SAAU3kB,EAAOtL,GACpC,GAAI4wB,EAEJ,KAAKzwB,UAAU3C,OAAQ,CACrB,GAAIf,KAAKgN,QACP,IAAK6B,IAAS7O,MAAKsX,QACjB6c,EAAMn0B,KAAKsX,QAAQzI,GACfslB,GACFiC,EAAoBp2B,KAAM6O,GAAQslB,EAAIpzB,OAK5C,OADAf,MAAKsX,WACEtX,KAIT,GADAm0B,EAAMn0B,KAAKsX,QAAQzI,IACdslB,EACH,MAAOn0B,KAET,IAAyB,IAArB0D,UAAU3C,OAGZ,MAFAq1B,GAAoBp2B,KAAM6O,GAAQslB,EAAIpzB,QACtCf,KAAKsX,QAAQzI,GAAS,KACf7O,IAKT,KAFA,GAAI8F,GACAhF,EAAIqzB,EAAIpzB,OACLD,KAEL,GADAgF,EAAKquB,EAAIrzB,GACLgF,IAAOvC,GAAMuC,EAAGvC,KAAOA,EAAI,CAC7B6yB,EAAoBp2B,KAAM6O,GAAO,GACjCslB,EAAIqC,OAAO11B,EAAG,EACd,OAGJ,MAAOd,OAUTD,EAAI4U,UAAUkd,MAAQ,SAAUhjB,GAC9B,GAAI4nB,GAA4B,gBAAV5nB,EACtBA,GAAQ4nB,EAAW5nB,EAAQA,EAAM9F,IACjC,IAAIorB,GAAMn0B,KAAKsX,QAAQzI,GACnB6nB,EAAkBD,IAAatC,CACnC,IAAIA,EAAK,CACPA,EAAMA,EAAIpzB,OAAS,EAAI6C,EAAQuwB,GAAOA,CAKtC,IAAIwC,GAAeF,GAAYtC,EAAIyC,KAAK,SAAU9wB,GAChD,MAAOA,GAAGirB,aAER4F,KACFD,GAAkB,EAGpB,KAAK,GADDxxB,GAAOtB,EAAQF,UAAW,GACrB5C,EAAI,EAAG2C,EAAI0wB,EAAIpzB,OAAQD,EAAI2C,EAAG3C,IAAK,CAC1C,GAAIgF,GAAKquB,EAAIrzB,GACT0S,EAAM1N,EAAGnC,MAAM3D,KAAMkF,EACrBsO,MAAQ,GAAUmjB,IAAgB7wB,EAAGirB,cACvC2F,GAAkB,IAIxB,MAAOA,IAUT32B,EAAI4U,UAAUkiB,WAAa,SAAUhoB,GACnC,GAAI4nB,GAA4B,gBAAV5nB,EAItB,IAHAA,EAAQ4nB,EAAW5nB,EAAQA,EAAM9F,KAG5B/I,KAAKuX,aAAa1I,GAAvB,CACA,GAAIwJ,GAAWrY,KAAK+W,UAChB7R,EAAOtB,EAAQF,UACf+yB,KAGFvxB,EAAK,IAAO6D,KAAM8F,EAAOioB,OAAQ92B,MAEnC,KAAK,GAAIc,GAAI,EAAG2C,EAAI4U,EAAStX,OAAQD,EAAI2C,EAAG3C,IAAK,CAC/C,GAAIqP,GAAQkI,EAASvX,GACjB41B,EAAkBvmB,EAAM0hB,MAAMluB,MAAMwM,EAAOjL,EAC3CwxB,IACFvmB,EAAM0mB,WAAWlzB,MAAMwM,EAAOjL,GAGlC,MAAOlF,QAUTD,EAAI4U,UAAUoiB,UAAY,SAAUloB,GAClC,GAAI6nB,GAAkB12B,KAAK6xB,MAAMluB,MAAM3D,KAAM0D,UAC7C,IAAKgzB,EAAL,CACA,GAAIhpB,GAAS1N,KAAKgN,QACd9H,EAAOtB,EAAQF,UAInB,KADAwB,EAAK,IAAO6D,KAAM8F,EAAOioB,OAAQ92B,MAC1B0N,GACLgpB,EAAkBhpB,EAAOmkB,MAAMluB,MAAM+J,EAAQxI,GAC7CwI,EAASgpB,EAAkBhpB,EAAOV,QAAU,IAE9C,OAAOhN,OAaT,IAAIs2B,GAAS,SAaf,QAASU,IAAcj3B,GAmCrB,QAASk3B,KACPj3B,KAAK8X,aAAc,EACnB9X,KAAK6X,UAAW,EAChB7X,KAAKyY,UAAU,SA3BjB1Y,EAAI4U,UAAUiE,OAAS,SAAU3M,GAC/B,IAAIjM,KAAK+M,YAgBT,MAZAd,GAAKiB,EAAMjB,GACNA,IACHA,EAAKkB,SAASwD,cAAc,QAE9B3Q,KAAKuyB,SAAStmB,GACdjM,KAAK2xB,gBACDtkB,EAAMrN,KAAK6W,MACb7W,KAAKyY,UAAU,YACfwe,EAAM31B,KAAKtB,OAEXA,KAAKu2B,MAAM,gBAAiBU,GAEvBj3B,MAqBTD,EAAI4U,UAAU2e,SAAW,SAAU9mB,EAAQwmB,GACzChzB,KAAK+yB,SAASvmB,EAAQwmB,IAcxBjzB,EAAI4U,UAAUuiB,SAAW,SAAUjrB,EAAI4U,EAAM7E,EAAOjK,GAClD,MAAOsQ,IAAQpW,EAAIjM,KAAKsY,UAAU,GAAMtY,KAAMiM,EAAI4U,EAAM7E,EAAOjK,IAkBnE,QAAShS,IAAI2S,GACX1S,KAAK4W,MAAMlE,GAoHb,QAASykB,IAAQvxB,EAAK8c,EAAGmR,GAGvB,MAFAA,GAASA,EAASlQ,SAASkQ,EAAQ,IAAM,EACzCnR,EAAIxgB,EAASwgB,GACO,gBAANA,GAAiB9c,EAAIlD,MAAMmxB,EAAQA,EAASnR,GAAK9c,EAWjE,QAASwxB,IAASxxB,EAAKyxB,EAAQC,GAE7B,GADA1xB,EAAM2xB,GAAa3xB,GACL,MAAVyxB,EACF,MAAOzxB,EAET,IAAsB,kBAAXyxB,GACT,MAAOzxB,GAAIkD,OAAOuuB,EAGpBA,IAAU,GAAKA,GAAQl0B,aAQvB,KAAK,GADDq0B,GAAMr3B,EAAKC,EAAKgqB,EAJhB1H,EAAkB,OAAd4U,EAAqB,EAAI,EAE7BlzB,EAAOJ,MAAM2Q,UAAUkP,OAAOlgB,SAAUC,EAAQF,UAAWgf,IAC3DlP,KAEK1S,EAAI,EAAG2C,EAAImC,EAAI7E,OAAQD,EAAI2C,EAAG3C,IAIrC,GAHA02B,EAAO5xB,EAAI9E,GACXV,EAAMo3B,GAAQA,EAAKC,QAAUD,EAC7BpN,EAAIhmB,EAAKrD,QAEP,KAAOqpB,KAEL,GADAjqB,EAAMiE,EAAKgmB,GACC,SAARjqB,GAAkB0N,GAAS2pB,EAAKE,KAAML,IAAWxpB,GAAS4M,GAAQra,EAAKD,GAAMk3B,GAAS,CACxF7jB,EAAI5K,KAAK4uB,EACT,YAGK3pB,IAAS2pB,EAAMH,IACxB7jB,EAAI5K,KAAK4uB,EAGb,OAAOhkB,GAUT,QAASmkB,IAAQ/xB,GAiCf,QAASgyB,GAAYp1B,EAAGC,EAAGo1B,GACzB,GAAIC,GAAUC,EAASF,EASvB,OARIC,KACc,SAAZA,IACExzB,EAAS9B,IAAM,UAAYA,KAAGA,EAAIA,EAAEi1B,QACpCnzB,EAAS7B,IAAM,UAAYA,KAAGA,EAAIA,EAAEg1B,SAE1Cj1B,EAAI8B,EAAS9B,GAAKiY,GAAQjY,EAAGs1B,GAAWt1B,EACxCC,EAAI6B,EAAS7B,GAAKgY,GAAQhY,EAAGq1B,GAAWr1B,GAEnCD,IAAMC,EAAI,EAAID,EAAIC,EAAIu1B,GAASA,EA1CxC,GAAIC,GAAa,KACbF,EAAWpxB,MACff,GAAM2xB,GAAa3xB,EAGnB,IAAIV,GAAOtB,EAAQF,UAAW,GAC1Bs0B,EAAQ9yB,EAAKA,EAAKnE,OAAS,EACV,iBAAVi3B,IACTA,EAAQA,EAAQ,GAAI,EAAK,EACzB9yB,EAAOA,EAAKnE,OAAS,EAAImE,EAAKxC,MAAM,GAAG,GAAMwC,GAE7C8yB,EAAQ,CAIV,IAAIE,GAAWhzB,EAAK,EACpB,OAAKgzB,IAE0B,kBAAbA,GAEhBD,EAAa,SAAUz1B,EAAGC,GACxB,MAAOy1B,GAAS11B,EAAGC,GAAKu1B,IAI1BD,EAAW/zB,MAAM2Q,UAAUkP,OAAOlgB,SAAUuB,GAC5C+yB,EAAa,SAAUz1B,EAAGC,EAAG3B,GAE3B,MADAA,GAAIA,GAAK,EACFA,GAAKi3B,EAASh3B,OAAS,EAAI62B,EAAYp1B,EAAGC,EAAG3B,GAAK82B,EAAYp1B,EAAGC,EAAG3B,IAAMm3B,EAAWz1B,EAAGC,EAAG3B,EAAI,KAkBnG8E,EAAIlD,QAAQioB,KAAKsN,IA7BfryB,EAuCX,QAASiI,IAASzN,EAAKi3B,GACrB,GAAIv2B,EACJ,IAAIyD,EAAcnE,GAAM,CACtB,GAAIgE,GAAOC,OAAOD,KAAKhE,EAEvB,KADAU,EAAIsD,EAAKrD,OACFD,KACL,GAAI+M,GAASzN,EAAIgE,EAAKtD,IAAKu2B,GACzB,OAAO,MAGN,IAAItjB,GAAQ3T,IAEjB,IADAU,EAAIV,EAAIW,OACDD,KACL,GAAI+M,GAASzN,EAAIU,GAAIu2B,GACnB,OAAO,MAGN,IAAW,MAAPj3B,EACT,MAAOA,GAAI6B,WAAWkB,cAAcwC,QAAQ0xB,IAAU,EAwH1D,QAASc,IAAkBp4B,GAsGzB,QAASq4B,GAAYrvB,GAEnB,MAAO,IAAI+S,UAAS,mBAAqB1Y,EAAS2F,GAAQ,wCA9F5DhJ,EAAI2S,SACFia,WAAYA,GACZ0L,kBAAmBA,GACnB1vB,QAASA,GACT2vB,eACA3kB,cACA4kB,YACA31B,SAAS,GAOX7C,EAAIy4B,KAAOA,GACXz4B,EAAIoK,OAASA,GACbpK,EAAIE,IAAMA,EACVF,EAAY,OAAIoB,EAChBpB,EAAIud,SAAWA,GAMfvd,EAAI04B,SAAWA,GACf14B,EAAIkiB,gBAAkBA,GACtBliB,EAAIqtB,mBAAqBA,GACzBrtB,EAAI24B,SACFzf,KAAMA,GACNnO,KAAMA,GACNyV,SAAUA,GACVoY,UAAWA,GACX9uB,WAAYA,IASd9J,EAAImiB,IAAM,CACV,IAAIA,GAAM,CAQVniB,GAAIkE,OAAS,SAAU20B,GACrBA,EAAgBA,KAChB,IAAIC,GAAQ74B,KACR84B,EAA8B,IAAdD,EAAM3W,GAC1B,IAAI4W,GAAiBF,EAAcG,MACjC,MAAOH,GAAcG,KAEvB,IAAIhwB,GAAO6vB,EAAc7vB,MAAQ8vB,EAAMnmB,QAAQ3J,KAE3CiwB,EAAMZ,EAAYrvB,GAAQ,eAqB9B,OApBAiwB,GAAIrkB,UAAYtQ,OAAOwC,OAAOgyB,EAAMlkB,WACpCqkB,EAAIrkB,UAAU4D,YAAcygB,EAC5BA,EAAI9W,IAAMA,IACV8W,EAAItmB,QAAUyB,GAAa0kB,EAAMnmB,QAASkmB,GAC1CI,EAAW,MAAIH,EAEfG,EAAI/0B,OAAS40B,EAAM50B,OAGnBkG,GAAO8uB,YAAYpQ,QAAQ,SAAU7U,GACnCglB,EAAIhlB,GAAQ6kB,EAAM7kB,KAGhBjL,IACFiwB,EAAItmB,QAAQiB,WAAW5K,GAAQiwB,GAG7BF,IACFF,EAAcG,MAAQC,GAEjBA,GAwBTj5B,EAAIm5B,IAAM,SAAUC,GAElB,IAAIA,EAAOC,UAAX,CAIA,GAAIl0B,GAAOtB,EAAQF,UAAW,EAQ9B,OAPAwB,GAAKm0B,QAAQr5B,MACiB,kBAAnBm5B,GAAOG,QAChBH,EAAOG,QAAQ31B,MAAMw1B,EAAQj0B,GAE7Bi0B,EAAOx1B,MAAM,KAAMuB,GAErBi0B,EAAOC,WAAY,EACZp5B,OAQTD,EAAI0U,MAAQ,SAAUA,GACpB1U,EAAI2S,QAAUyB,GAAapU,EAAI2S,QAAS+B,IAW1CtK,GAAO8uB,YAAYpQ,QAAQ,SAAU7U,GACnCjU,EAAIiU,GAAQ,SAAUhB,EAAIumB,GACxB,MAAKA,IAKU,cAATvlB,GAAwBzP,EAAcg1B,KACnCA,EAAWxwB,OACdwwB,EAAWxwB,KAAOiK,GAEpBumB,EAAax5B,EAAIkE,OAAOs1B,IAE1Bv5B,KAAK0S,QAAQsB,EAAO,KAAKhB,GAAMumB,EACxBA,GAXAv5B,KAAK0S,QAAQsB,EAAO,KAAKhB,MAiBtC/O,EAAOlE,EAAI4M,WAAYA,IAhtTzB,GAAItL,IAAiBgD,OAAOsQ,UAAUtT,eAoBlCI,GAAiB,iDA8EjBoB,GAAa,SAiBbK,GAAc,iBAkBdG,GAAa,oBA4EbpB,GAAWoC,OAAOsQ,UAAU1S,SAC5BuC,GAAgB,kBAahBuP,GAAU/P,MAAM+P,QAsGhBuB,GAAY,gBAGZkkB,GAA8B,mBAAXC,SAAqE,oBAA3Cp1B,OAAOsQ,UAAU1S,SAASX,KAAKm4B,QAG5E1c,GAAWyc,IAAaC,OAAOC,6BAG/BC,GAAKH,IAAaC,OAAOG,UAAUC,UAAU12B,cAC7C22B,GAAOH,IAAMA,GAAGh0B,QAAQ,WAAa,EACrC6J,GAAQmqB,IAAMA,GAAGh0B,QAAQ,YAAc,EACvCo0B,GAAYJ,IAAMA,GAAGh0B,QAAQ,WAAa,EAC1Cq0B,GAAQL,IAAM,uBAAuBj4B,KAAKi4B,IAE1CM,GAAiBtzB,OACjBmG,GAAqBnG,OACrBuzB,GAAgBvzB,OAChBwzB,GAAoBxzB,MAGxB,IAAI6yB,KAAchqB,GAAO,CACvB,GAAI4qB,IAA2CzzB,SAA3B8yB,OAAOY,iBAAkE1zB,SAAjC8yB,OAAOa,sBAC/DC,GAAyC5zB,SAA1B8yB,OAAOe,gBAAgE7zB,SAAhC8yB,OAAOgB,oBACjER,IAAiBG,GAAgB,mBAAqB,aACtDttB,GAAqBstB,GAAgB,sBAAwB,gBAC7DF,GAAgBK,GAAe,kBAAoB,YACnDJ,GAAoBI,GAAe,qBAAuB,eAmB5D,GAAIjd,IAAW,WAKb,QAASod,KACPC,GAAU,CACV,IAAIC,GAASC,EAAUn4B,MAAM,EAC7Bm4B,GAAU95B,OAAS,CACnB,KAAK,GAAID,GAAI,EAAGA,EAAI85B,EAAO75B,OAAQD,IACjC85B,EAAO95B,KATX,GAAI+5B,MACAF,GAAU,EACVG,EAAYn0B,MAkBhB,IAAuB,mBAAZo0B,UAA2B30B,EAAS20B,SAAU,CACvD,GAAIC,GAAID,QAAQE,UACZngB,EAAO,YACXggB,GAAY,WACVE,EAAEE,KAAKR,GAMHV,IAAOt0B,WAAWoV,QAEnB,IAAgC,mBAArBqgB,kBAAkC,CAGlD,GAAIC,GAAU,EACVC,EAAW,GAAIF,kBAAiBT,GAChCY,EAAWnuB,SAASkE,eAAeuW,OAAOwT,GAC9CC,GAAStlB,QAAQulB,GACfC,eAAe,IAEjBT,EAAY,WACVM,GAAWA,EAAU,GAAK,EAC1BE,EAASxqB,KAAO8W,OAAOwT,QAKzBN,GAAYp1B,UAGd,OAAO,UAAUI,EAAItC,GACnB,GAAIuB,GAAOvB,EAAM,WACfsC,EAAGxE,KAAKkC,IACNsC,CACJ+0B,GAAUjyB,KAAK7D,GACX41B,IACJA,GAAU,EACVG,EAAUJ,EAAiB,QAI3Bzc,GAAOtX,MAEQ,oBAAR60B,MAAuBp1B,EAASo1B,KAEzCvd,GAAOud,KAGPvd,GAAO,WACLje,KAAKC,IAAMoE,OAAOwC,OAAO,OAE3BoX,GAAKtJ,UAAU8H,IAAM,SAAUtc,GAC7B,MAAyBwG,UAAlB3G,KAAKC,IAAIE,IAElB8d,GAAKtJ,UAAU9E,IAAM,SAAU1P,GAC7BH,KAAKC,IAAIE,GAAO,GAElB8d,GAAKtJ,UAAU+J,MAAQ,WACrB1e,KAAKC,IAAMoE,OAAOwC,OAAO,OAW7B,IAAIm0B,IAAI10B,EAAMqO,SAadqmB,IAAElxB,IAAM,SAAU3J,EAAK6B,GACrB,GAAIiQ,GAEAwpB,EAAQz7B,KAAK2J,IAAIxJ,GAAK,EAoB1B,OAnBKs7B,KACCz7B,KAAKwG,OAASxG,KAAKuG,QACrB0L,EAAUjS,KAAK07B,SAEjBD,GACEt7B,IAAKA,GAEPH,KAAK4G,QAAQzG,GAAOs7B,EAChBz7B,KAAK0G,MACP1G,KAAK0G,KAAKi1B,MAAQF,EAClBA,EAAMG,MAAQ57B,KAAK0G,MAEnB1G,KAAKyG,KAAOg1B,EAEdz7B,KAAK0G,KAAO+0B,EACZz7B,KAAKwG,QAEPi1B,EAAMz5B,MAAQA,EAEPiQ,GAST+oB,GAAEU,MAAQ,WACR,GAAID,GAAQz7B,KAAKyG,IAQjB,OAPIg1B,KACFz7B,KAAKyG,KAAOzG,KAAKyG,KAAKk1B,MACtB37B,KAAKyG,KAAKm1B,MAAQj1B,OAClB80B,EAAME,MAAQF,EAAMG,MAAQj1B,OAC5B3G,KAAK4G,QAAQ60B,EAAMt7B,KAAOwG,OAC1B3G,KAAKwG,QAEAi1B,GAYTT,GAAErxB,IAAM,SAAUxJ,EAAK07B,GACrB,GAAIJ,GAAQz7B,KAAK4G,QAAQzG,EACzB,IAAcwG,SAAV80B,EACJ,MAAIA,KAAUz7B,KAAK0G,KACVm1B,EAAcJ,EAAQA,EAAMz5B,OAMjCy5B,EAAME,QACJF,IAAUz7B,KAAKyG,OACjBzG,KAAKyG,KAAOg1B,EAAME,OAEpBF,EAAME,MAAMC,MAAQH,EAAMG,OAExBH,EAAMG,QACRH,EAAMG,MAAMD,MAAQF,EAAME,OAE5BF,EAAME,MAAQh1B,OACd80B,EAAMG,MAAQ57B,KAAK0G,KACf1G,KAAK0G,OACP1G,KAAK0G,KAAKi1B,MAAQF,GAEpBz7B,KAAK0G,KAAO+0B,EACLI,EAAcJ,EAAQA,EAAMz5B,OAGrC,IAOIJ,IACAgI,GACA1C,GACAH,GACAO,GACAe,GAZAqB,GAAU,GAAIpD,GAAM,KACpB8C,GAAgB,cAYhBd,GAAa,EACbE,GAAc,EACdC,GAAkB,EAClBF,GAAiB,EAEjBhB,GAAY,GACZC,GAAY,GACZY,GAAU,IACVL,GAAY,GACZX,GAAW,GAEXM,IAAgBo0B,GAAM,EAAGC,IAAM,EAAGC,GAAM,GACxCp0B,IAAek0B,GAAM,GAAMC,IAAM,IAAMC,GAAM,IA0N7CrD,GAAYt0B,OAAO43B,QACrB1yB,eAAgBA,IAGdS,GAAgB,yBAChBY,GAAQjE,OACR8D,GAAQ9D,OACRgE,GAAShE,OA+HToF,GAAW,aAiBXjB,GAAOzG,OAAO43B,QAChBhyB,aAAcA,EACdY,UAAWA,EACXU,YAAaA,IAGXnB,IAAc,KAAM,MACpBG,IAAoB,MAAO,OAE3BJ,GAAS9F,OAAO63B,kBASlB/qB,OAAO,EAQPgrB,QAAQ,EAMRC,OAAO,EAOPC,sBAAsB,EAOtBtf,UAAU,EASVuf,oBAAoB,EAQpBrD,aAAc,YAAa,YAAa,mBAAoB,SAAU,aAAc,WAMpFsD,mBACExW,QAAS,EACTC,QAAS,EACTC,SAAU,GAOZuW,gBAAiB,MAGjBpyB,YAOET,IAAK,WACH,MAAOS,KAETnK,IAAK,SAAaG,GAChBgK,GAAahK,EACb6J,KAEFpF,cAAc,EACdH,YAAY,GAEd6F,kBACEZ,IAAK,WACH,MAAOY,KAETtK,IAAK,SAAaG,GAChBmK,GAAmBnK,EACnB6J,KAEFpF,cAAc,EACdH,YAAY,KAIZ+3B,GAAO91B,OA8EPgG,GAAatI,OAAO43B,QACtBjwB,qBAAsBA,EACtBK,qBAAsBA,EACtBE,qBAAsBA,EACtBJ,gBAAiBA,IAuVfwF,GAAQ,UA6FRiB,GAAc,mJACdC,GAAgB,8BAgEhByB,GAASnK,GAAOuyB,sBAAwBr4B,OAAOwC,OAAO,KAwB1DyN,IAAOxD,KAAO,SAAUwC,EAAWC,EAAUvS,GAC3C,MAAKA,GAoBMsS,GAAaC,EACf,WAEL,GAAIopB,GAAmC,kBAAbppB,GAA0BA,EAASjS,KAAKN,GAAMuS,EACpEqpB,EAAmC,kBAAdtpB,GAA2BA,EAAUhS,KAAKN,GAAM2F,MACzE,OAAIg2B,GACKzpB,GAAUypB,EAAcC,GAExBA,GARN,OAlBArpB,EAGmB,kBAAbA,GAEFD,EAEJA,EAQE,WACL,MAAOJ,IAAUK,EAASjS,KAAKtB,MAAOsT,EAAUhS,KAAKtB,QAR9CuT,EAPAD,GAmCbgB,GAAOrI,GAAK,SAAUqH,EAAWC,EAAUvS,GACzC,GAAKA,IAAMuS,GAAgC,kBAAbA,GAA9B,CAIA,GAAIxP,GAAMwP,GAAYD,CAEtB,OAAOtS,IAAqB,kBAAR+C,GAAqBA,EAAIzC,KAAKN,GAAM+C,IAO1DuQ,GAAO6O,KAAO7O,GAAOuoB,QAAUvoB,GAAO2iB,MAAQ3iB,GAAOwoB,SAAWxoB,GAAOyoB,SAAWzoB,GAAO0oB,cAAgB1oB,GAAO2oB,SAAW3oB,GAAO4oB,cAAgB5oB,GAAO6oB,UAAY7oB,GAAO8oB,SAAW,SAAU9pB,EAAWC,GAC1M,MAAOA,GAAWD,EAAYA,EAAUuQ,OAAOtQ,GAAYQ,GAAQR,GAAYA,GAAYA,GAAYD,GAgBzGnJ,GAAO8uB,YAAYpQ,QAAQ,SAAU7U,GACnCM,GAAON,EAAO,KAAOX,KAUvBiB,GAAOod,MAAQpd,GAAOmd,OAAS,SAAUne,EAAWC,GAClD,IAAKA,EAAU,MAAOD,EACtB,KAAKA,EAAW,MAAOC,EACvB,IAAIxP,KACJE,GAAOF,EAAKuP,EACZ,KAAK,GAAInT,KAAOoT,GAAU,CACxB,GAAI7F,GAAS3J,EAAI5D,GACbgQ,EAAQoD,EAASpT,EACjBuN,KAAWqG,GAAQrG,KACrBA,GAAUA,IAEZ3J,EAAI5D,GAAOuN,EAASA,EAAOmW,OAAO1T,IAAUA,GAE9C,MAAOpM,IAOTuQ,GAAOR,MAAQQ,GAAOmc,QAAUnc,GAAOic,SAAW,SAAUjd,EAAWC,GACrE,IAAKA,EAAU,MAAOD,EACtB,KAAKA,EAAW,MAAOC,EACvB,IAAIxP,GAAMM,OAAOwC,OAAO,KAGxB,OAFA5C,GAAOF,EAAKuP,GACZrP,EAAOF,EAAKwP,GACLxP,EAOT,IAAIwQ,IAAe,SAAsBjB,EAAWC,GAClD,MAAoB5M,UAAb4M,EAAyBD,EAAYC,GAkK1CyB,GAAQ,CAgBZD,IAAI7I,OAAS,KAQb6I,GAAIJ,UAAU0oB,OAAS,SAAUC,GAC/Bt9B,KAAKiV,KAAKrM,KAAK00B,IASjBvoB,GAAIJ,UAAU4oB,UAAY,SAAUD,GAClCt9B,KAAKiV,KAAKoe,QAAQiK,IAOpBvoB,GAAIJ,UAAU6B,OAAS,WACrBzB,GAAI7I,OAAOsxB,OAAOx9B,OAOpB+U,GAAIJ,UAAU/T,OAAS,WAGrB,IAAK,GADDqU,GAAOrR,EAAQ5D,KAAKiV,MACfnU,EAAI,EAAG2C,EAAIwR,EAAKlU,OAAQD,EAAI2C,EAAG3C,IACtCmU,EAAKnU,GAAGwvB,SAIZ,IAAImN,IAAaz5B,MAAM2Q,UACnBc,GAAepR,OAAOwC,OAAO42B,KAM/B,OAAQ,MAAO,QAAS,UAAW,SAAU,OAAQ,WAAW5U,QAAQ,SAAUpH,GAElF,GAAI7G,GAAW6iB,GAAWhc,EAC1Bhd,GAAIgR,GAAcgM,EAAQ,WAKxB,IAFA,GAAI3gB,GAAI4C,UAAU3C,OACdmE,EAAO,GAAIlB,OAAMlD,GACdA,KACLoE,EAAKpE,GAAK4C,UAAU5C,EAEtB,IAEIkgB,GAFA3b,EAASuV,EAASjX,MAAM3D,KAAMkF,GAC9B1E,EAAKR,KAAKS,MAEd,QAAQghB,GACN,IAAK,OACHT,EAAW9b,CACX,MACF,KAAK,UACH8b,EAAW9b,CACX,MACF,KAAK,SACH8b,EAAW9b,EAAKxC,MAAM,GAM1B,MAHIse,IAAUxgB,EAAGmV,aAAaqL,GAE9BxgB,EAAGG,IAAIC,SACAyE,MAaXZ,EAAIg5B,GAAY,OAAQ,SAAc12B,EAAO3G,GAI3C,MAHI2G,IAAS/G,KAAKe,SAChBf,KAAKe,OAASqB,OAAO2E,GAAS,GAEzB/G,KAAKw2B,OAAOzvB,EAAO,EAAG3G,GAAK,KASpCqE,EAAIg5B,GAAY,UAAW,SAAiBjG,GAE1C,GAAKx3B,KAAKe,OAAV,CACA,GAAIgG,GAAQpB,EAAQ3F,KAAMw3B,EAC1B,OAAIzwB,IAAQ,EACH/G,KAAKw2B,OAAOzvB,EAAO,GAD5B,SAKF,IAAI2O,IAAYrR,OAAOq5B,oBAAoBjoB,IAYvCN,IAAgB,CAyCpBC,IAAST,UAAUiB,KAAO,SAAU1V,GAElC,IAAK,GADDkE,GAAOC,OAAOD,KAAKlE,GACdY,EAAI,EAAG2C,EAAIW,EAAKrD,OAAQD,EAAI2C,EAAG3C,IACtCd,KAAKU,QAAQ0D,EAAKtD,GAAIZ,EAAIkE,EAAKtD,MAUnCsU,GAAST,UAAUgB,aAAe,SAAUgoB,GAC1C,IAAK,GAAI78B,GAAI,EAAG2C,EAAIk6B,EAAM58B,OAAQD,EAAI2C,EAAG3C,IACvCiV,GAAQ4nB,EAAM78B,KAYlBsU,GAAST,UAAUjU,QAAU,SAAUP,EAAKC,GAC1C8V,GAAelW,KAAKgC,MAAO7B,EAAKC,IAYlCgV,GAAST,UAAUsB,MAAQ,SAAUjV,IAClChB,KAAKa,MAAQb,KAAKa,SAAW+H,KAAK5H,IAUrCoU,GAAST,UAAU0b,SAAW,SAAUrvB,GACtChB,KAAKa,IAAIwyB,QAAQryB,GAuHnB,IAAIw3B,IAAOn0B,OAAO43B,QACjB/lB,eAAgBA,GAChBjW,IAAKA,EACLkB,IAAKA,EACLd,OAAQA,EACRkB,UAAWA,EACXI,WAAYA,EACZI,UAAWA,EACXG,SAAUA,EACVI,UAAWA,EACXC,YAAaA,EACbI,SAAUA,EACVM,UAAWA,EACXG,SAAUA,EACVE,KAAMA,EACNM,QAASA,EACTK,OAAQA,EACRK,SAAUA,EACVC,cAAeA,EACfE,IAAKA,EACLm5B,SAAU94B,EACVa,QAASA,EACTE,YAAaA,EACbI,WAAYA,EACZ8N,QAASA,GACTuB,SAAUA,GACVkkB,UAAWA,GACXzc,SAAUA,GACV+c,KAAMA,GACNtqB,MAAOA,GACPuqB,UAAWA,GACXC,MAAOA,GACPC,GAAIA,kBAAoB,MAAOA,KAC/BntB,GAAIA,sBAAwB,MAAOA,KACnCotB,GAAIA,iBAAmB,MAAOA,KAC9BC,GAAIA,qBAAuB,MAAOA,KAClC7c,SAAUA,GACVW,GAAIA,QAAU,MAAOA,KACrB/Q,MAAOA,EACPG,MAAOA,EACPS,QAASA,EACTI,YAAaA,EACbC,YAAaA,GACb7B,OAAQA,GACRgC,MAAOA,GACP9B,OAAQA,GACRiC,QAASA,GACT7L,QAASA,GACTgM,GAAIA,GACJI,IAAKA,GACLM,SAAUA,GACVK,SAAUA,GACVI,YAAaA,GACbE,eAAgBA,GAChBQ,SAAUA,GACVJ,WAAYA,GACZW,aAAcA,GACdO,QAASA,GACTK,aAAcA,GACdE,gBAAiBA,GACjBxB,WAAYA,GACZ8B,aAAcA,GACd+B,aAAcA,GACdpB,aAAcA,GACdN,mBAAoBA,GACpBG,YAAaA,GACbC,cAAeA,GACf4pB,KAAMA,KAGHplB,GAAM,EAsGNmD,GAAY,GAAIlU,GAAM,KAGtBqT,GAAS,EACTI,GAAO,EACPC,GAAqB,EACrBC,GAAgB,EAGhBJ,GAAc,EACdgkB,GAAU,EACVC,GAAe,EACfC,GAAW,EACX7jB,GAAc,EACdX,GAAkB,EAClBC,GAAkB,EAClBa,GAAa,EACbD,GAAQ,EAERD,KAEJA,IAAiBN,KACfmkB,IAAOnkB,IACPokB,OAAUF,GAAUpkB,IACpBukB,KAAMhkB,IACNjT,KAAQoT,KAGVF,GAAiB0jB,KACfG,IAAOH,IACPM,KAAML,IACNI,KAAMhkB,IACNjT,KAAQoT,KAGVF,GAAiB2jB,KACfE,IAAOF,IACPG,OAAUF,GAAUpkB,KAGtBQ,GAAiB4jB,KACfE,OAAUF,GAAUpkB,IACpBykB,GAAML,GAAUpkB,IAChB0kB,QAAWN,GAAUpkB,IACrBqkB,IAAOH,GAAS9jB,IAChBokB,KAAML,GAAc/jB,IACpBmkB,KAAMhkB,GAAaH,IACnB9S,KAAQoT,GAAYN,KAGtBI,GAAiBD,KACfokB,KAAM/kB,GAAiBI,IACvB4kB,KAAM/kB,GAAiBG,IACvBukB,KAAMhkB,GAAaF,IACnBwkB,KAAMX,GAAS5jB,IACfhT,IAAOmT,GACPqkB,MAASvkB,GAAaP,KAGxBQ,GAAiBZ,KACf+kB,KAAMpkB,GAAaP,IACnB1S,IAAOmT,GACPqkB,MAASllB,GAAiBI,KAG5BQ,GAAiBX,KACf+kB,KAAMrkB,GAAaP,IACnB1S,IAAOmT,GACPqkB,MAASjlB,GAAiBG,IAmP5B,IAAIV,IAAO5U,OAAO43B,QAChB1hB,UAAWA,GACXE,QAASA,GACTE,QAASA,KAGPuB,GAAkB,GAAI5V,GAAM,KAE5Bo4B,GAAkB,qJAClBtjB,GAAoB,GAAI1Q,QAAO,KAAOg0B,GAAgB97B,QAAQ,KAAM,QAAU,QAG9E+7B,GAAmB,mQACnBnjB,GAAqB,GAAI9Q,QAAO,KAAOi0B,GAAiB/7B,QAAQ,KAAM,QAAU,QAEhF+Y,GAAO,MACPT,GAAY,MACZQ,GAAS,6IACTL,GAAY,WACZe,GAAa,6FACbR,GAAU,gCACVS,GAAmB,+CAcnBpB,MAgKApR,GAAaxF,OAAO43B,QACtB9zB,gBAAiBuS,GACjByB,aAAcA,KAUZI,MACAC,MACAC,MACAC,MACAC,IAAU,EAuFVe,GAAQ,CA0DZH,IAAQ5I,UAAUhL,IAAM,WACtB3J,KAAK4+B,WACL,IACI58B,GADAga,EAAQhc,KAAKgc,OAAShc,KAAKgB,EAE/B,KACEgB,EAAQhC,KAAKqW,OAAO/U,KAAK0a,EAAOA,GAChC,MAAOvF,IAkBT,MAbIzW,MAAK20B,MACPpW,GAASvc,GAEPhC,KAAK6+B,aACP78B,EAAQhC,KAAK6+B,WAAW78B,IAEtBhC,KAAK2I,UACP3G,EAAQga,EAAM0X,cAAc1xB,EAAO,KAAMhC,KAAK2I,SAAS,IAErD3I,KAAK8+B,cACP98B,EAAQhC,KAAK8+B,YAAY98B,IAE3BhC,KAAK++B,WACE/8B,GASTub,GAAQ5I,UAAU1U,IAAM,SAAU+B,GAChC,GAAIga,GAAQhc,KAAKgc,OAAShc,KAAKgB,EAC3BhB,MAAK2I,UACP3G,EAAQga,EAAM0X,cAAc1xB,EAAOhC,KAAKgC,MAAOhC,KAAK2I,SAAS,GAE/D,KACE3I,KAAKsW,OAAOhV,KAAK0a,EAAOA,EAAOha,GAC/B,MAAOyU,IAIT,GAAIuoB,GAAahjB,EAAMijB,WACvB,IAAID,GAAcA,EAAWE,QAAUl/B,KAAK6J,WAAY,CACtD,GAAIm1B,EAAWr2B,QAEb,MAEFq2B,GAAWG,UAAU,WACfnjB,EAAM0b,KAERsH,EAAWnY,SAAS7K,EAAM0b,MAAQ11B,EAElCg9B,EAAWnY,SAAShM,KAAKmB,EAAMojB,OAAQp9B,OAU/Cub,GAAQ5I,UAAUiqB,UAAY,WAC5B7pB,GAAI7I,OAASlM,MASfud,GAAQ5I,UAAU6oB,OAAS,SAAU78B,GACnC,GAAIqS,GAAKrS,EAAIqS,EACRhT,MAAKke,UAAUzB,IAAIzJ,KACtBhT,KAAKke,UAAUrO,IAAImD,GACnBhT,KAAK+d,QAAQnV,KAAKjI,GACbX,KAAKge,OAAOvB,IAAIzJ,IACnBrS,EAAI08B,OAAOr9B,QASjBud,GAAQ5I,UAAUoqB,SAAW,WAC3BhqB,GAAI7I,OAAS,IAEb,KADA,GAAIpL,GAAId,KAAK8d,KAAK/c,OACXD,KAAK,CACV,GAAIH,GAAMX,KAAK8d,KAAKhd,EACfd,MAAKke,UAAUzB,IAAI9b,EAAIqS,KAC1BrS,EAAI48B,UAAUv9B,MAGlB,GAAIq/B,GAAMr/B,KAAKge,MACfhe,MAAKge,OAAShe,KAAKke,UACnBle,KAAKke,UAAYmhB,EACjBr/B,KAAKke,UAAUQ,QACf2gB,EAAMr/B,KAAK8d,KACX9d,KAAK8d,KAAO9d,KAAK+d,QACjB/d,KAAK+d,QAAUshB,EACfr/B,KAAK+d,QAAQhd,OAAS,GAUxBwc,GAAQ5I,UAAU2b,OAAS,SAAUhS,GAC/Bte,KAAK6d,KACP7d,KAAK4d,OAAQ,EACJ5d,KAAK40B,OAASzqB,GAAOiyB,MAC9Bp8B,KAAKkd,OAILld,KAAKse,QAAUte,KAAKqe,SAASC,GAAUte,KAAKse,UAAoBA,EAChEte,KAAKqe,QAAS,EAIdlB,GAAYnd,QAShBud,GAAQ5I,UAAUuI,IAAM,WACtB,GAAIld,KAAK2d,OAAQ,CACf,GAAI3b,GAAQhC,KAAK2J,KACjB,IAAI3H,IAAUhC,KAAKgC,QAKlBsC,EAAStC,IAAUhC,KAAK20B,QAAU30B,KAAKse,QAAS,CAE/C,GAAIqV,GAAW3zB,KAAKgC,KACpBhC,MAAKgC,MAAQA,CAIGhC,MAAKme,SAGnBne,MAAK8F,GAAGxE,KAAKtB,KAAKgB,GAAIgB,EAAO2xB,GAGjC3zB,KAAKqe,OAASre,KAAKse,SAAU,IASjCf,GAAQ5I,UAAU+a,SAAW,WAG3B,GAAI4P,GAAUvqB,GAAI7I,MAClBlM,MAAKgC,MAAQhC,KAAK2J,MAClB3J,KAAK4d,OAAQ,EACb7I,GAAI7I,OAASozB,GAOf/hB,GAAQ5I,UAAU6B,OAAS,WAEzB,IADA,GAAI1V,GAAId,KAAK8d,KAAK/c,OACXD,KACLd,KAAK8d,KAAKhd,GAAG0V,UAQjB+G,GAAQ5I,UAAU4e,SAAW,WAC3B,GAAIvzB,KAAK2d,OAAQ,CAKV3d,KAAKgB,GAAG+W,mBAAsB/X,KAAKgB,GAAGgX,eACzChY,KAAKgB,GAAGkW,UAAUmc,QAAQrzB,KAG5B,KADA,GAAIc,GAAId,KAAK8d,KAAK/c,OACXD,KACLd,KAAK8d,KAAKhd,GAAGy8B,UAAUv9B,KAEzBA,MAAK2d,QAAS,EACd3d,KAAKgB,GAAKhB,KAAK8F,GAAK9F,KAAKgC,MAAQ,MAYrC,IAAIyc,IAAc,GAAIR,IA8BlBshB,IAEFj8B,KAAM,WACJtD,KAAKylB,KAA4B,IAArBzlB,KAAKiM,GAAG2B,SAAiB,OAAS,eAGhD0iB,OAAQ,SAAgBtuB,GACtBhC,KAAKiM,GAAGjM,KAAKylB,MAAQ1jB,EAAUC,KAI/Bkd,GAAgB,GAAI5Y,GAAM,KAC1Bma,GAAkB,GAAIna,GAAM,KAE5BkF,IACFkU,QAAS,EAAG,GAAI,IAChB8f,QAAS,EAAG,aAAc,eAC1BC,IAAK,EAAG,iBAAkB,oBAC1BC,KAAM,EAAG,mCAAoC,uBAG/Cl0B,IAAIm0B,GAAKn0B,GAAIo0B,IAAM,EAAG,qBAAsB,yBAE5Cp0B,GAAIq0B,OAASr0B,GAAIs0B,UAAY,EAAG,+BAAgC,aAEhEt0B,GAAIu0B,MAAQv0B,GAAIw0B,MAAQx0B,GAAIy0B,SAAWz0B,GAAI00B,QAAU10B,GAAI20B,OAAS,EAAG,UAAW,YAEhF30B,GAAI40B,EAAI50B,GAAI60B,KAAO70B,GAAI80B,OAAS90B,GAAI0tB,IAAM1tB,GAAI+0B,MAAQ/0B,GAAIV,KAAOU,GAAIg1B,OAASh1B,GAAIi1B,QAAUj1B,GAAIk1B,KAAOl1B,GAAIyN,KAAOzN,GAAIm1B,QAAUn1B,GAAIo1B,SAAWp1B,GAAIwd,MAAQ,EAAG,gJAAqK,SAcnU,IAAI5J,IAAU,aACVE,GAAW,WACXE,GAAY,OA2FZW,GAAoB,WAEtB,GAAIqZ,GAAW,CACb,GAAIh3B,GAAI2K,SAASwD,cAAc,MAE/B,OADAnO,GAAEgQ,UAAY,0BACNhQ,EAAE+P,WAAU,GAAM7D,WAAW8D,UAErC,OAAO,KAKP6N,GAAsB,WAExB,GAAImZ,GAAW,CACb,GAAIqH,GAAI1zB,SAASwD,cAAc,WAE/B,OADAkwB,GAAEC,YAAc,IACmB,MAA5BD,EAAEtuB,WAAU,GAAMvQ,MAEzB,OAAO,KA4GPue,GAAWlc,OAAO43B,QACpB1pB,UAAWA,GACX+N,cAAeA,KAGbtV,IAEF1H,KAAM,WAGqB,IAArBtD,KAAKiM,GAAG2B,WAEV5N,KAAKmS,SAELnS,KAAKkR,OAASF,GAAa,UAC3BpO,GAAQ5C,KAAKiM,GAAIjM,KAAKkR,UAI1Bof,OAAQ,SAAgBtuB,GACtBA,EAAQD,EAAUC,GACdhC,KAAKmS,MACPnS,KAAK+gC,KAAK/+B,GAEVhC,KAAKiM,GAAGuG,UAAYxQ,GAIxB++B,KAAM,SAAc/+B,GAGlB,IADA,GAAIlB,GAAId,KAAKmS,MAAMpR,OACZD,KACL0L,GAAOxM,KAAKmS,MAAMrR,GAIpB,IAAIiR,GAAOuO,GAActe,GAAO,GAAM,EAEtChC,MAAKmS,MAAQvO,EAAQmO,EAAKmP,YAC1B5U,GAAOyF,EAAM/R,KAAKkR,SAqDtByP,IAAShM,UAAU+M,SAAW,SAAUkQ,GACtC,GAAI9wB,GAAG2C,CACP,KAAK3C,EAAI,EAAG2C,EAAIzD,KAAK+gB,WAAWhgB,OAAQD,EAAI2C,EAAG3C,IAC7Cd,KAAK+gB,WAAWjgB,GAAG4gB,SAASkQ,EAE9B,KAAK9wB,EAAI,EAAG2C,EAAIzD,KAAKqY,SAAStX,OAAQD,EAAI2C,EAAG3C,IAC3C8wB,EAAK5xB,KAAKqY,SAASvX,KA6EvB6f,GAAShM,UAAUmN,aAAe,WAChC,GAAIhhB,GAAG2C,CACP,KAAK3C,EAAI,EAAG2C,EAAIzD,KAAK+gB,WAAWhgB,OAAQD,EAAI2C,EAAG3C,IAG7Cd,KAAK+gB,WAAWjgB,GAAGghB,cAAa,EAElC,KAAKhhB,EAAI,EAAG2C,EAAIzD,KAAKqY,SAAStX,OAAQD,EAAI2C,EAAG3C,IAM3Cd,KAAKqY,SAASvX,GAAGwyB,UAAS,GAAO,EAEnC,IAAIxJ,GAAO9pB,KAAKihB,OAAO6I,IACvB,KAAKhpB,EAAI,EAAG2C,EAAIqmB,EAAK/oB,OAAQD,EAAI2C,EAAG3C,IAIlCgpB,EAAKhpB,GAAGkgC,UAAYlX,EAAKhpB,GAAGkgC,SAASzN,YAQzC5S,GAAShM,UAAUqN,QAAU,WACvBhiB,KAAK8gB,YACP9gB,KAAK8gB,WAAWC,WAAWsS,QAAQrzB,MAErCA,KAAKsN,KAAKiU,SAAW,KACrBvhB,KAAKihB,SA2BP,IAAImB,IAAc,GAAI9b,GAAM,IA4C5B2b,IAAgBtN,UAAU9N,OAAS,SAAUga,EAAM7E,EAAO8E,GACxD,GAAI/O,GAAOQ,GAAUvS,KAAKugB,SAC1B,OAAO,IAAII,IAAS3gB,KAAK4gB,OAAQ5gB,KAAKgB,GAAI+Q,EAAM8O,EAAM7E,EAAO8E,GAG/D,IAAImgB,IAAK,IACLC,GAAQ,IACRC,GAAO,IACPC,GAAa,KACbC,GAAK,KACLC,GAAY,KACZC,GAAU,KACVC,GAAK,KACLC,GAAM,KACNC,GAAO,KAEPC,GAAQ,EAERC,IAEFrX,SAAUkX,GACV9X,UAAU,EAEVkY,QAAS,WAAY,UAAW,gBAAiB,iBAEjDv+B,KAAM,WAIJ,GAAIw+B,GAAU9hC,KAAK6J,WAAWkB,MAAM,sBACpC,IAAI+2B,EAAS,CACX,GAAIC,GAAUD,EAAQ,GAAG/2B,MAAM,gBAC3Bg3B,IACF/hC,KAAKgiC,SAAWD,EAAQ,GAAG/4B,OAC3BhJ,KAAKk/B,MAAQ6C,EAAQ,GAAG/4B,QAExBhJ,KAAKk/B,MAAQ4C,EAAQ,GAAG94B,OAE1BhJ,KAAK6J,WAAai4B,EAAQ,GAG5B,GAAK9hC,KAAKk/B,MAAV,CAMAl/B,KAAKgT,GAAK,eAAgB2uB,EAQ1B,IAAIr2B,GAAMtL,KAAKiM,GAAG8E,OAClB/Q,MAAKiiC,UAAoB,WAAR32B,GAA4B,aAARA,IAAsD,WAA/BtL,KAAKiM,GAAG0B,WAAWoD,QAG/E/Q,KAAK8D,MAAQkN,GAAa,eAC1BhR,KAAK6R,IAAMb,GAAa,aACxBpO,GAAQ5C,KAAKiM,GAAIjM,KAAK6R,KACtBvF,GAAOtM,KAAK8D,MAAO9D,KAAK6R,KAGxB7R,KAAK4K,MAAQvG,OAAOwC,OAAO,MAG3B7G,KAAKN,QAAU,GAAIuiB,IAAgBjiB,KAAKgB,GAAIhB,KAAKiM,MAGnDqkB,OAAQ,SAAgBxf,GACtB9Q,KAAKkiC,KAAKpxB,GACV9Q,KAAKmiC,YACLniC,KAAKoiC,eAiBPF,KAAM,SAAcpxB,GAElB,GAYIhQ,GAAG2C,EAAGsO,EAAM5R,EAAK6B,EAAOqgC,EAZxB7K,EAAO1mB,EAAK,GACZwxB,EAAsBtiC,KAAKuiC,WAAaj+B,EAASkzB,IAASn3B,EAAOm3B,EAAM,SAAWn3B,EAAOm3B,EAAM,UAE/F1U,EAAa9iB,KAAK6hC,OAAOW,QACzBC,EAAWziC,KAAK0iC,MAChBA,EAAQ1iC,KAAK0iC,MAAQ,GAAI1+B,OAAM8M,EAAK/P,QACpCm+B,EAAQl/B,KAAKk/B,MACb8C,EAAWhiC,KAAKgiC,SAChBl+B,EAAQ9D,KAAK8D,MACb+N,EAAM7R,KAAK6R,IACXokB,EAAa5oB,EAAMvJ,GACnBqf,GAAQsf,CAOZ,KAAK3hC,EAAI,EAAG2C,EAAIqN,EAAK/P,OAAQD,EAAI2C,EAAG3C,IAClC02B,EAAO1mB,EAAKhQ,GACZX,EAAMmiC,EAAsB9K,EAAKE,KAAO,KACxC11B,EAAQsgC,EAAsB9K,EAAKC,OAASD,EAC5C6K,GAAa/9B,EAAStC,GACtB+P,GAAQoR,GAAQnjB,KAAK2iC,cAAc3gC,EAAOlB,EAAGX,GACzC4R,GAEFA,EAAK6wB,QAAS,EAEd7wB,EAAKiK,MAAMojB,OAASt+B,EAEhBX,IACF4R,EAAKiK,MAAM0b,KAAOv3B,GAGhB6hC,IACFjwB,EAAKiK,MAAMgmB,GAAoB,OAAR7hC,EAAeA,EAAMW,IAI1CgiB,GAAcwf,GAAuBD,IACvCntB,GAAkB,WAChBnD,EAAKiK,MAAMkjB,GAASl9B,MAKxB+P,EAAO/R,KAAK6G,OAAO7E,EAAOk9B,EAAOp+B,EAAGX,GACpC4R,EAAK8wB,OAAS1f,GAEhBuf,EAAM5hC,GAAKiR,EACPoR,GACFpR,EAAKzF,OAAOuF,EAKhB,KAAIsR,EAAJ,CAOA,GAAI2f,GAAe,EACfC,EAAeN,EAAS1hC,OAAS2hC,EAAM3hC,MAK3C,KADAf,KAAKgB,GAAGgX,eAAgB,EACnBlX,EAAI,EAAG2C,EAAIg/B,EAAS1hC,OAAQD,EAAI2C,EAAG3C,IACtCiR,EAAO0wB,EAAS3hC,GACXiR,EAAK6wB,SACR5iC,KAAKgjC,iBAAiBjxB,GACtB/R,KAAKwM,OAAOuF,EAAM+wB,IAAgBC,EAAc9M,GAGpDj2B,MAAKgB,GAAGgX,eAAgB,EACpB8qB,IACF9iC,KAAKgB,GAAGkW,UAAYlX,KAAKgB,GAAGkW,UAAUpO,OAAO,SAAUm6B,GACrD,MAAOA,GAAEtlB,SAMb,IAAIulB,GAAYC,EAAQC,EACpBC,EAAiB,CACrB,KAAKviC,EAAI,EAAG2C,EAAIi/B,EAAM3hC,OAAQD,EAAI2C,EAAG3C,IACnCiR,EAAO2wB,EAAM5hC,GAEboiC,EAAaR,EAAM5hC,EAAI,GACvBqiC,EAASD,EAAaA,EAAWI,UAAYJ,EAAWK,cAAgBL,EAAWrxB,KAAOqxB,EAAW51B,KAAOxJ,EACxGiO,EAAK6wB,SAAW7wB,EAAKuxB,WACvBF,EAAc9gB,GAAavQ,EAAMjO,EAAO9D,KAAKgT,IACzCowB,IAAgBF,GAAgBE,GAGpC9gB,GAAa8gB,EAAat/B,EAAO9D,KAAKgT,MAAQkwB,GAC5CljC,KAAKwjC,KAAKzxB,EAAMoxB,IAKlBnjC,KAAKo1B,OAAOrjB,EAAMsxB,IAAkBF,EAAQlN,GAE9ClkB,EAAK6wB,OAAS7wB,EAAK8wB,OAAQ,IAc/Bh8B,OAAQ,SAAgB7E,EAAOk9B,EAAOn4B,EAAO5G,GAC3C,GAAI0gB,GAAO7gB,KAAKmyB,MAEZsR,EAAczjC,KAAKmY,QAAUnY,KAAKgB,GAClCgb,EAAQ3X,OAAOwC,OAAO48B,EAE1BznB,GAAMhF,MAAQ3S,OAAOwC,OAAO48B,EAAYzsB,OACxCgF,EAAM/E,KAAO5S,OAAOwC,OAAO48B,EAAYxsB,MAEvC+E,EAAMhP,QAAUy2B,EAEhBznB,EAAMijB,YAAcj/B,KAIpBkV,GAAkB,WAChBgB,GAAe8F,EAAOkjB,EAAOl9B,KAE/BkU,GAAe8F,EAAO,SAAUjV,GAC5B5G,EACF+V,GAAe8F,EAAO,OAAQ7b,GACrB6b,EAAM0b,MAEfjzB,EAAIuX,EAAO,OAAQ,MAEjBhc,KAAKgiC,UACP9rB,GAAe8F,EAAOhc,KAAKgiC,SAAkB,OAAR7hC,EAAeA,EAAM4G,EAE5D,IAAIgL,GAAO/R,KAAKN,QAAQmH,OAAOga,EAAM7E,EAAOhc,KAAKoY,MAGjD,OAFArG,GAAKyQ,MAAQxiB,KAAKgT,GAClBhT,KAAK0jC,UAAU1hC,EAAO+P,EAAMhL,EAAO5G,GAC5B4R,GAOTowB,UAAW,WACT,GAAIhV,GAAMntB,KAAKwqB,WAAW2C,GAC1B,IAAKA,EAAL,CACA,GACImF,GADApB,GAAQlxB,KAAKmY,QAAUnY,KAAKgB,IAAIgW,KAE/BhX,MAAKuiC,YAGRjQ,KACAtyB,KAAK0iC,MAAM7Z,QAAQ,SAAU9W,GAC3BugB,EAAKvgB,EAAKiK,MAAM0b,MAAQ3U,GAAehR,MAJzCugB,EAAOtyB,KAAK0iC,MAAMl3B,IAAIuX,IAOxBmO,EAAK/D,GAAOmF,IAQd8P,YAAa,WACX,GAAIpiC,KAAKiiC,SAAU,CACjB,GAAIv0B,GAAS1N,KAAK8D,MAAM6J,WACpBg2B,EAAQj2B,GAAUA,EAAOk2B,SACzBD,IACFA,EAAME,gBAcZzO,OAAQ,SAAgBrjB,EAAMhL,EAAOo8B,EAAQlN,GACvClkB,EAAKuxB,YACPvxB,EAAKuxB,UAAUt9B,SACf+L,EAAKuxB,UAAY,KAEnB,IAAIQ,GAAgB9jC,KAAK+jC,WAAWhyB,EAAMhL,EAAO,KAAM,QACvD,IAAIkvB,GAAc6N,EAAe,CAI/B,GAAI5yB,GAASa,EAAKwxB,aACbryB,KACHA,EAASa,EAAKwxB,cAAgBvyB,GAAa,kBAC3CE,EAAOqQ,SAAWxP,GAEpBzD,GAAM4C,EAAQiyB,EACd,IAAIz2B,GAAKqF,EAAKuxB,UAAYz9B,EAAY,WACpCkM,EAAKuxB,UAAY,KACjBvxB,EAAKzF,OAAO4E,GACZ1E,GAAO0E,IAETxL,YAAWgH,EAAIo3B,OACV,CACL,GAAI53B,GAASi3B,EAAO50B,WAEfrC,KAGHoC,GAAMtO,KAAK6R,IAAKsxB,GAChBj3B,EAASlM,KAAK6R,KAEhBE,EAAKzF,OAAOJ,KAahBM,OAAQ,SAAgBuF,EAAMhL,EAAOue,EAAO2Q,GAC1C,GAAIlkB,EAAKuxB,UAQP,MAPAvxB,GAAKuxB,UAAUt9B,cACf+L,EAAKuxB,UAAY,KAQnB,IAAIQ,GAAgB9jC,KAAK+jC,WAAWhyB,EAAMhL,EAAOue,EAAO,QACxD,IAAI2Q,GAAc6N,EAAe,CAC/B,GAAIp3B,GAAKqF,EAAKuxB,UAAYz9B,EAAY,WACpCkM,EAAKuxB,UAAY,KACjBvxB,EAAKvF,UAEP9G,YAAWgH,EAAIo3B,OAEf/xB,GAAKvF,UAYTg3B,KAAM,SAAczxB,EAAMoxB,GAMnBA,EAAO50B,aACVvO,KAAK6R,IAAIlE,WAAWvB,YAAYpM,KAAK6R,KAEvCE,EAAKzF,OAAO62B,EAAO50B,aAAa,IAYlCm1B,UAAW,SAAmB1hC,EAAO+P,EAAMhL,EAAO5G,GAChD,GAGI6S,GAHA8P,EAAa9iB,KAAK6hC,OAAOW,QACzB53B,EAAQ5K,KAAK4K,MACby3B,GAAa/9B,EAAStC,EAEtB7B,IAAO2iB,GAAcuf,GACvBrvB,EAAK6P,GAAc9b,EAAO5G,EAAK6B,EAAO8gB,GACjClY,EAAMoI,KACTpI,EAAMoI,GAAMjB,KAKdiB,EAAKhT,KAAKgT,GACN3S,EAAO2B,EAAOgR,GACE,OAAdhR,EAAMgR,KACRhR,EAAMgR,GAAMjB,GAIL1N,OAAO2R,aAAahU,IAC7ByC,EAAIzC,EAAOgR,EAAIjB,IAGnBA,EAAKuI,IAAMtY,GAYb2gC,cAAe,SAAuB3gC,EAAO+E,EAAO5G,GAClD,GAEI4R,GAFA+Q,EAAa9iB,KAAK6hC,OAAOW,QACzBH,GAAa/9B,EAAStC,EAE1B,IAAI7B,GAAO2iB,GAAcuf,EAAW,CAClC,GAAIrvB,GAAK6P,GAAc9b,EAAO5G,EAAK6B,EAAO8gB,EAC1C/Q,GAAO/R,KAAK4K,MAAMoI,OAElBjB,GAAO/P,EAAMhC,KAAKgT,GAKpB,OAHIjB,KAASA,EAAK6wB,QAAU7wB,EAAK8wB,OAG1B9wB,GASTixB,iBAAkB,SAA0BjxB,GAC1C,GAAI/P,GAAQ+P,EAAKuI,IACbwI,EAAa9iB,KAAK6hC,OAAOW,QACzBxmB,EAAQjK,EAAKiK,MACbjV,EAAQiV,EAAMojB,OAGdj/B,EAAME,EAAO2b,EAAO,SAAWA,EAAM0b,KACrC2K,GAAa/9B,EAAStC,EAC1B,IAAI8gB,GAAc3iB,GAAOkiC,EAAW,CAClC,GAAIrvB,GAAK6P,GAAc9b,EAAO5G,EAAK6B,EAAO8gB,EAC1C9iB,MAAK4K,MAAMoI,GAAM,SAEjBhR,GAAMhC,KAAKgT,IAAM,KACjBjB,EAAKuI,IAAM,MAafypB,WAAY,SAAoBhyB,EAAMhL,EAAOue,EAAOtR,GAClDA,GAAc,SACd,IAAIgwB,GAAQjyB,EAAKzE,KAAKV,UAClBC,EAAQm3B,GAASA,EAAMn3B,MACvB+kB,EAAO/kB,IAAUA,EAAMmH,IAASnH,EAAMo3B,QAC1C,OAAOrS,GAAOA,EAAKtwB,KAAKyQ,EAAMhL,EAAOue,GAASve,EAAQ4c,SAAS3jB,KAAK6hC,OAAO7tB,IAAShU,KAAK6hC,OAAOoC,QAAS,KAQ3GC,YAAa,SAAqBliC,GAGhC,MADAhC,MAAK6mB,SAAW7kB,EACTA,GAYTmiC,aAAc,SAAsBniC,GAClC,GAAI+R,GAAQ/R,GACV,MAAOA,EACF,IAAIuC,EAAcvC,GAAQ,CAM/B,IAJA,GAGI7B,GAHAiE,EAAOC,OAAOD,KAAKpC,GACnBlB,EAAIsD,EAAKrD,OACTyS,EAAM,GAAIxP,OAAMlD,GAEbA,KACLX,EAAMiE,EAAKtD,GACX0S,EAAI1S,IACF42B,KAAMv3B,EACNs3B,OAAQz1B,EAAM7B,GAGlB,OAAOqT,GAKP,MAHqB,gBAAVxR,IAAuBK,MAAML,KACtCA,EAAQygB,GAAMzgB,IAETA,OAIXoiC,OAAQ,WAIN,GAHIpkC,KAAKwqB,WAAW2C,OACjBntB,KAAKmY,QAAUnY,KAAKgB,IAAIgW,MAAMhX,KAAKwqB,WAAW2C,KAAO,MAEpDntB,KAAK0iC,MAGP,IAFA,GACI3wB,GADAjR,EAAId,KAAK0iC,MAAM3hC,OAEZD,KACLiR,EAAO/R,KAAK0iC,MAAM5hC,GAClBd,KAAKgjC,iBAAiBjxB,GACtBA,EAAKiQ,YAmFTqiB,IAEF9Z,SAAUiX,GACV7X,UAAU,EAEVrmB,KAAM,WACJ,GAAI2I,GAAKjM,KAAKiM,EACd,IAAKA,EAAG+W,QAYNhjB,KAAKskC,SAAU,MAZA,CAEf,GAAIt9B,GAAOiF,EAAGs4B,kBACVv9B,IAAoC,OAA5B8G,EAAQ9G,EAAM,YACxBwF,GAAOxF,GACPhH,KAAKwkC,OAASx9B,GAGhBhH,KAAKkR,OAASF,GAAa,QAC3BpO,GAAQqJ,EAAIjM,KAAKkR,UAOrBof,OAAQ,SAAgBtuB,GAClBhC,KAAKskC,UACLtiC,EACGhC,KAAK+R,MACR/R,KAAKo1B,SAGPp1B,KAAKwM,WAIT4oB,OAAQ,WACFp1B,KAAKykC,WACPzkC,KAAKykC,SAASj4B,SACdxM,KAAKykC,SAAW,MAGbzkC,KAAKN,UACRM,KAAKN,QAAU,GAAIuiB,IAAgBjiB,KAAKgB,GAAIhB,KAAKiM,KAEnDjM,KAAK+R,KAAO/R,KAAKN,QAAQmH,OAAO7G,KAAKmyB,MAAOnyB,KAAKmY,OAAQnY,KAAKoY,OAC9DpY,KAAK+R,KAAKzF,OAAOtM,KAAKkR,SAGxB1E,OAAQ,WACFxM,KAAK+R,OACP/R,KAAK+R,KAAKvF,SACVxM,KAAK+R,KAAO,MAEV/R,KAAKwkC,SAAWxkC,KAAKykC,WAClBzkC,KAAK0kC,cACR1kC,KAAK0kC,YAAc,GAAIziB,IAAgBjiB,KAAKwkC,OAAOtsB,UAAYlY,KAAKgB,GAAIhB,KAAKwkC,SAE/ExkC,KAAKykC,SAAWzkC,KAAK0kC,YAAY79B,OAAO7G,KAAKmyB,MAAOnyB,KAAKmY,OAAQnY,KAAKoY,OACtEpY,KAAKykC,SAASn4B,OAAOtM,KAAKkR,UAI9BkzB,OAAQ,WACFpkC,KAAK+R,MACP/R,KAAK+R,KAAKiQ,UAERhiB,KAAKykC,UACPzkC,KAAKykC,SAASziB,YAKhB2iB,IAEFrhC,KAAM,WAEJ,GAAI0D,GAAOhH,KAAKiM,GAAGs4B,kBACfv9B,IAAoC,OAA5B8G,EAAQ9G,EAAM,YACxBhH,KAAKwkC,OAASx9B,IAIlBspB,OAAQ,SAAgBtuB,GACtBhC,KAAK2D,MAAM3D,KAAKiM,GAAIjK,GAChBhC,KAAKwkC,QACPxkC,KAAK2D,MAAM3D,KAAKwkC,QAASxiC,IAI7B2B,MAAO,SAAesI,EAAIjK,GAMxB,QAAS4iC,KACP34B,EAAG4Y,MAAMggB,QAAU7iC,EAAQ,GAAK,OAN9BqL,EAAMpB,GACRE,EAAgBF,EAAIjK,EAAQ,GAAI,EAAI4iC,EAAQ5kC,KAAKgB,IAEjD4jC,MAQFE,IAEFxhC,KAAM,WACJ,GAAIue,GAAO7hB,KACPiM,EAAKjM,KAAKiM,GACV84B,EAAsB,UAAZ94B,EAAG+H,KACb6J,EAAO7d,KAAK6hC,OAAOhkB,KACnBwgB,EAASr+B,KAAK6hC,OAAOxD,OACrBT,EAAW59B,KAAK6hC,OAAOjE,SASvBoH,GAAY,CAkEhB,IAjEKjL,IAAcgL,IACjB/kC,KAAK4O,GAAG,mBAAoB,WAC1Bo2B,GAAY,IAEdhlC,KAAK4O,GAAG,iBAAkB,WACxBo2B,GAAY,EAMPnnB,GACHgE,EAAKojB,cAOXjlC,KAAKklC,SAAU,EACVH,GAAYlnB,IACf7d,KAAK4O,GAAG,QAAS,WACfiT,EAAKqjB,SAAU,IAEjBllC,KAAK4O,GAAG,OAAQ,WACdiT,EAAKqjB,SAAU,EAEVrjB,EAAKzJ,QAASyJ,EAAKzJ,MAAM4I,UAC5Ba,EAAKsjB,iBAMXnlC,KAAKilC,SAAWjlC,KAAKmlC,YAAc,WACjC,IAAIH,GAAcnjB,EAAKoQ,OAAvB,CAGA,GAAI7xB,GAAMi+B,GAAU0G,EAAU7iC,EAAS+J,EAAGjK,OAASiK,EAAGjK,KACtD6f,GAAK5hB,IAAIG,GAGTkd,GAAS,WACHuE,EAAKoQ,SAAWpQ,EAAKqjB,SACvBrjB,EAAKyO,OAAOzO,EAAKmf,SAASh/B,WAM5B47B,IACF59B,KAAKilC,SAAWngC,EAAU9E,KAAKilC,SAAUrH,IAa3C59B,KAAKolC,UAA8B,kBAAXC,QACpBrlC,KAAKolC,UAAW,CAClB,GAAI3jB,GAAS4jB,OAAO9hC,GAAGqL,GAAK,KAAO,MACnCy2B,QAAOp5B,GAAIwV,GAAQ,SAAUzhB,KAAKmlC,aAC7BtnB,GACHwnB,OAAOp5B,GAAIwV,GAAQ,QAASzhB,KAAKilC,cAGnCjlC,MAAK4O,GAAG,SAAU5O,KAAKmlC,aAClBtnB,GACH7d,KAAK4O,GAAG,QAAS5O,KAAKilC,WAKrBpnB,GAAQrO,KACXxP,KAAK4O,GAAG,MAAO,WACb0O,GAASuE,EAAKojB,YAEhBjlC,KAAK4O,GAAG,QAAS,SAAU6H,GACP,KAAdA,EAAEqN,SAAgC,IAAdrN,EAAEqN,SACxBjC,EAAKojB,eAMPh5B,EAAGmC,aAAa,UAA2B,aAAfnC,EAAG8E,SAA0B9E,EAAGjK,MAAMgH,UACpEhJ,KAAKslC,UAAYtlC,KAAKilC,WAI1B3U,OAAQ,SAAgBtuB,GAGtBA,EAAQD,EAAUC,GACdA,IAAUhC,KAAKiM,GAAGjK,QAAOhC,KAAKiM,GAAGjK,MAAQA,IAG/CoiC,OAAQ,WACN,GAAIn4B,GAAKjM,KAAKiM,EACd,IAAIjM,KAAKolC,UAAW,CAClB,GAAI3jB,GAAS4jB,OAAO9hC,GAAGyL,IAAM,MAAQ,QACrCq2B,QAAOp5B,GAAIwV,GAAQ,SAAUzhB,KAAKilC,UAClCI,OAAOp5B,GAAIwV,GAAQ,QAASzhB,KAAKilC,aAKnCM,IAEFjiC,KAAM,WACJ,GAAIue,GAAO7hB,KACPiM,EAAKjM,KAAKiM,EAEdjM,MAAKijB,SAAW,WAEd,GAAIhX,EAAG5K,eAAe,UACpB,MAAO4K,GAAGoX,MAEZ,IAAIjjB,GAAM6L,EAAGjK,KAIb,OAHI6f,GAAKggB,OAAOxD,SACdj+B,EAAM8B,EAAS9B,IAEVA,GAGTJ,KAAKilC,SAAW,WACdpjB,EAAK5hB,IAAI4hB,EAAKoB,aAEhBjjB,KAAK4O,GAAG,SAAU5O,KAAKilC,UAEnBh5B,EAAGmC,aAAa,aAClBpO,KAAKslC,UAAYtlC,KAAKilC,WAI1B3U,OAAQ,SAAgBtuB,GACtBhC,KAAKiM,GAAGu5B,QAAUv/B,EAAWjE,EAAOhC,KAAKijB,cAIzCwiB,IAEFniC,KAAM,WACJ,GAAIoiC,GAAQ1lC,KAER6hB,EAAO7hB,KACPiM,EAAKjM,KAAKiM,EAGdjM,MAAK6jC,YAAc,WACbhiB,EAAKmf,UACPnf,EAAKyO,OAAOzO,EAAKmf,SAASr3B,OAK9B,IAAIg8B,GAAW3lC,KAAK2lC,SAAW15B,EAAGmC,aAAa,WAG/CpO,MAAKilC,SAAW,WACd,GAAIjjC,GAAQihB,GAAShX,EAAI05B,EACzB3jC,GAAQ6f,EAAKggB,OAAOxD,OAAStqB,GAAQ/R,GAASA,EAAMwJ,IAAItJ,GAAYA,EAASF,GAASA,EACtF6f,EAAK5hB,IAAI+B,IAEXhC,KAAK4O,GAAG,SAAU5O,KAAKilC,SAGvB,IAAIW,GAAY3iB,GAAShX,EAAI05B,GAAU,IACnCA,GAAYC,EAAU7kC,SAAW4kC,GAA0B,OAAdC,KAC/C5lC,KAAKslC,UAAYtlC,KAAKilC,UAOxBjlC,KAAKgB,GAAGgwB,IAAI,gBAAiB,WAC3B1T,GAASooB,EAAM7B,eAEZx2B,EAAMpB,IACTqR,GAAStd,KAAK6jC,cAIlBvT,OAAQ,SAAgBtuB,GACtB,GAAIiK,GAAKjM,KAAKiM,EACdA,GAAG45B,eAAgB,CAKnB,KAJA,GAGIn5B,GAAItM,EAHJ8iB,EAAQljB,KAAK2lC,UAAY5xB,GAAQ/R,GACjC0Q,EAAUzG,EAAGyG,QACb5R,EAAI4R,EAAQ3R,OAETD,KACL4L,EAAKgG,EAAQ5R,GACbV,EAAMsM,EAAGrL,eAAe,UAAYqL,EAAG2W,OAAS3W,EAAG1K,MAEnD0K,EAAG0W,SAAWF,EAAQI,GAAUthB,EAAO5B,IAAO,EAAK6F,EAAWjE,EAAO5B,IAKzEgkC,OAAQ,WAENpkC,KAAKgB,GAAGwyB,KAAK,gBAAiBxzB,KAAK6jC,eAiDnCiC,IAEFxiC,KAAM,WAQJ,QAASyiC,KACP,GAAI3lC,GAAM6L,EAAGu5B,OACb,OAAIplC,IAAO6L,EAAG5K,eAAe,cACpB4K,EAAG+5B,YAEP5lC,GAAO6L,EAAG5K,eAAe,eACrB4K,EAAGg6B,YAEL7lC,EAfT,GAAIyhB,GAAO7hB,KACPiM,EAAKjM,KAAKiM,EAEdjM,MAAKijB,SAAW,WACd,MAAOhX,GAAG5K,eAAe,UAAY4K,EAAGoX,OAASxB,EAAKggB,OAAOxD,OAASn8B,EAAS+J,EAAGjK,OAASiK,EAAGjK,OAchGhC,KAAKilC,SAAW,WACd,GAAItB,GAAQ9hB,EAAKmf,SAASr3B,KAC1B,IAAIoK,GAAQ4vB,GAAQ,CAClB,GAAIvjC,GAAMyhB,EAAKoB,WACXniB,EAAI6E,EAAQg+B,EAAOvjC,EACnB6L,GAAGu5B,QACD1kC,EAAI,GACN+gB,EAAK5hB,IAAI0jC,EAAM9f,OAAOzjB,IAEfU,GAAI,GACb+gB,EAAK5hB,IAAI0jC,EAAMjhC,MAAM,EAAG5B,GAAG+iB,OAAO8f,EAAMjhC,MAAM5B,EAAI,SAGpD+gB,GAAK5hB,IAAI8lC,MAIb/lC,KAAK4O,GAAG,SAAU5O,KAAKilC,UACnBh5B,EAAGmC,aAAa,aAClBpO,KAAKslC,UAAYtlC,KAAKilC,WAI1B3U,OAAQ,SAAgBtuB,GACtB,GAAIiK,GAAKjM,KAAKiM,EACV8H,IAAQ/R,GACViK,EAAGu5B,QAAU7/B,EAAQ3D,EAAOhC,KAAKijB,aAAc,EAE3ChX,EAAG5K,eAAe,cACpB4K,EAAGu5B,QAAUv/B,EAAWjE,EAAOiK,EAAG+5B,YAElC/5B,EAAGu5B,UAAYxjC,IAMnBmvB,IACFrmB,KAAMg6B,GACNS,MAAOA,GACPE,OAAQA,GACRK,SAAUA,IAGRnC,IAEFpZ,SAAU2W,GACV9iB,QAAQ,EACR+S,SAAUA,GACV0Q,QAAS,OAAQ,SAAU,YAa3Bv+B,KAAM,WAEJtD,KAAKkmC,eACDlmC,KAAKmmC,UAAYnmC,KAAKomC,QAG1B,IAEI5iB,GAFAvX,EAAKjM,KAAKiM,GACVX,EAAMW,EAAG8E,OAEb,IAAY,UAARzF,EACFkY,EAAU2N,GAASllB,EAAG+H,OAASmd,GAASrmB,SACnC,IAAY,WAARQ,EACTkY,EAAU2N,GAASsU,WACd,CAAA,GAAY,aAARn6B,EAIT,MAHAkY,GAAU2N,GAASrmB,KAKrBmB,EAAG23B,UAAY5jC,KACfwjB,EAAQlgB,KAAKhC,KAAKtB,MAClBA,KAAKswB,OAAS9M,EAAQ8M,OACtBtwB,KAAKqmC,QAAU7iB,EAAQ4gB,QAOzB8B,aAAc,WACZ,GAAIv9B,GAAU3I,KAAK2I,OACnB,IAAKA,EAEL,IADA,GAAI7H,GAAI6H,EAAQ5H,OACTD,KAAK,CACV,GAAIgI,GAASiK,GAAa/S,KAAKgB,GAAGsX,SAAU,UAAW3P,EAAQ7H,GAAGiI,OAC5C,kBAAXD,IAAyBA,EAAOgrB,QACzC9zB,KAAKmmC,SAAU,GAEbr9B,EAAO8qB,QACT5zB,KAAKomC,UAAW,KAKtBhC,OAAQ,WACNpkC,KAAKiM,GAAG23B,UAAY,KACpB5jC,KAAKqmC,SAAWrmC,KAAKqmC,YAKrBziB,IACF0iB,IAAK,GACLC,IAAK,EACLC,MAAO,GACPC,MAAO,GACPC,QAAW,EAAG,IACdC,GAAI,GACJhe,KAAM,GACNie,MAAO,GACPC,KAAM,IA+CJC,IAEFvc,SAAU0W,GACV8F,iBAAiB,EACjBnjB,SAAUA,GAEVtgB,KAAM,WAEJ,GAAwB,WAApBtD,KAAKiM,GAAG8E,SAAqC,SAAb/Q,KAAKkJ,IAAgB,CACvD,GAAI2Y,GAAO7hB,IACXA,MAAKgnC,WAAa,WAChBp4B,GAAGiT,EAAK5V,GAAGg7B,cAAeplB,EAAK3Y,IAAK2Y,EAAK2B,QAAS3B,EAAKwL,UAAU6Z,UAEnElnC,KAAK4O,GAAG,OAAQ5O,KAAKgnC,cAIzB1W,OAAQ,SAAgB9M,GAOtB,GAJKxjB,KAAKwqB,WAAWlQ,MACnBkJ,EAAU,cAGW,kBAAZA,GAAX,CAMIxjB,KAAKqtB,UAAU8Z,OACjB3jB,EAAUO,GAAWP,IAEnBxjB,KAAKqtB,UAAU+Z,UACjB5jB,EAAUS,GAAcT,IAEtBxjB,KAAKqtB,UAAUxL,OACjB2B,EAAUW,GAAWX,GAGvB,IAAIpf,GAAOC,OAAOD,KAAKpE,KAAKqtB,WAAWvkB,OAAO,SAAU3I,GACtD,MAAe,SAARA,GAA0B,YAARA,GAA6B,SAARA,GAA0B,YAARA,GAE9DiE,GAAKrD,SACPyiB,EAAUD,GAAUC,EAASpf,IAG/BpE,KAAKqnC,QACLrnC,KAAKwjB,QAAUA,EAEXxjB,KAAKgnC,WACPhnC,KAAKgnC,aAELp4B,GAAG5O,KAAKiM,GAAIjM,KAAKkJ,IAAKlJ,KAAKwjB,QAASxjB,KAAKqtB,UAAU6Z,WAIvDG,MAAO,WACL,GAAIp7B,GAAKjM,KAAKgnC,WAAahnC,KAAKiM,GAAGg7B,cAAgBjnC,KAAKiM,EACpDjM,MAAKwjB,SACPxU,GAAI/C,EAAIjM,KAAKkJ,IAAKlJ,KAAKwjB,UAI3B4gB,OAAQ,WACNpkC,KAAKqnC,UAILziB,IAAY,WAAY,QAAS,QACjCG,IAAiB,SAAU,MAAO,MAClCuiB,GAAc,gBACd/iB,GAAYlgB,OAAOwC,OAAO,MAE1B6d,GAAS,KAETG,IAEF8P,MAAM,EAENrE,OAAQ,SAAgBtuB,GACD,gBAAVA,GACThC,KAAKiM,GAAG4Y,MAAM0iB,QAAUvlC,EACf+R,GAAQ/R,GACjBhC,KAAKwnC,aAAaxlC,EAAMylC,OAAOxjC,OAE/BjE,KAAKwnC,aAAaxlC,QAItBwlC,aAAc,SAAsBxlC,GAGlC,GACI+G,GAAM3I,EADNwK,EAAQ5K,KAAK4K,QAAU5K,KAAK4K,SAEhC,KAAK7B,IAAQ6B,GACL7B,IAAQ/G,KACZhC,KAAK0nC,aAAa3+B,EAAM,YACjB6B,GAAM7B,GAGjB,KAAKA,IAAQ/G,GACX5B,EAAM4B,EAAM+G,GACR3I,IAAQwK,EAAM7B,KAChB6B,EAAM7B,GAAQ3I,EACdJ,KAAK0nC,aAAa3+B,EAAM3I,KAK9BsnC,aAAc,SAAsBpjB,EAAMtiB,GAExC,GADAsiB,EAAOD,GAAUC,GAIjB,GADa,MAATtiB,IAAeA,GAAS,IACxBA,EAAO,CACT,GAAI2lC,GAAcL,GAAY5lC,KAAKM,GAAS,YAAc,EACtD2lC,IAGF3lC,EAAQA,EAAMY,QAAQ0kC,GAAa,IAAIt+B,OACvChJ,KAAKiM,GAAG4Y,MAAM+iB,YAAYtjB,EAAKQ,MAAO9iB,EAAO2lC,IAE7C3nC,KAAKiM,GAAG4Y,MAAMP,EAAKE,OAASxiB;KAG9BhC,MAAKiM,GAAG4Y,MAAMP,EAAKE,OAAS,KA4D9BqjB,GAAU,+BACVC,GAAU,UAGVC,GAAyB,sGAGzBC,GAAkB,qCAGlBC,GAAmB,6CAInBC,IACFlmC,MAAO,SACPmmC,aAAc,aACdC,cAAe,eAGbC,IAEF9d,SAAU4W,GAEV79B,KAAM,WACJ,GAAImiB,GAAOzlB,KAAKkJ,IACZoC,EAAMtL,KAAKiM,GAAG8E,OAEb0U,KACHzlB,KAAK20B,MAAO,EAGd,IAAInK,GAAaxqB,KAAKwqB,WAClBrf,EAASqf,EAAW8D,MACpBnjB,KAEEqf,EAAW6D,aACbruB,KAAK6J,WAAa0B,EAAYJ,EAAQnL,KAAKmY,QAAUnY,KAAKgB,MAIxD+mC,GAAuBrmC,KAAK+jB,IAAkB,SAATA,IAA4B,YAARna,GAA6B,SAARA,MAEhFtL,KAAKiM,GAAGgC,gBAAgBwX,GACxBzlB,KAAKskC,SAAU,KAQrBhU,OAAQ,SAAgBtuB,GACtB,IAAIhC,KAAKskC,QAAT,CAGA,GAAI7e,GAAOzlB,KAAKkJ,GACZlJ,MAAKkJ,IACPlJ,KAAK0nC,aAAajiB,EAAMzjB,GAExBhC,KAAKwnC,aAAaxlC,SAKtBwlC,aAAc3iB,GAAM2iB,aAEpBE,aAAc,SAAsBjiB,EAAMzjB,GACxC,GAAIiK,GAAKjM,KAAKiM,GACVqiB,EAAStuB,KAAKwqB,WAAW8D,MAI7B,IAHItuB,KAAKqtB,UAAU7I,QACjBiB,EAAO9iB,EAAS8iB,KAEb6I,GAAU0Z,GAAgBtmC,KAAK+jB,IAASA,IAAQxZ,GAAI,CACvD,GAAIq8B,GAAqB,UAAT7iB,GAA4B,MAATzjB,EACjC,GAAaA,CAEXiK,GAAGwZ,KAAU6iB,IACfr8B,EAAGwZ,GAAQ6iB,GAIf,GAAIC,GAAYL,GAAWziB,EAC3B,KAAK6I,GAAUia,EAAW,CACxBt8B,EAAGs8B,GAAavmC,CAEhB,IAAI2hC,GAAQ13B,EAAG23B,SACXD,IACFA,EAAMsB,WAIV,MAAa,UAATxf,GAAmC,aAAfxZ,EAAG8E,YACzB9E,GAAGgC,gBAAgBwX,QAIjBwiB,GAAiBvmC,KAAK+jB,GACxBxZ,EAAGyD,aAAa+V,EAAMzjB,EAAQ,OAAS,SACrB,MAATA,GAAiBA,KAAU,EACvB,UAATyjB,GAGExZ,EAAGW,YACL5K,GAAS,IAAMiK,EAAGW,UAAUoG,GAAK,eAEnC1D,GAASrD,EAAIjK,IACJ8lC,GAAQpmC,KAAK+jB,GACtBxZ,EAAGu8B,eAAeX,GAASpiB,EAAMzjB,KAAU,EAAO,GAAKA,GAEvDiK,EAAGyD,aAAa+V,EAAMzjB,KAAU,EAAO,GAAKA,GAG9CiK,EAAGgC,gBAAgBwX,MAKrBxZ,IAEFse,SAAU8W,GAEV/9B,KAAM,WAEJ,GAAKtD,KAAKkJ,IAAV,CAGA,GAAI8J,GAAKhT,KAAKgT,GAAKrQ,EAAS3C,KAAKkJ,KAC7BopB,GAAQtyB,KAAKmY,QAAUnY,KAAKgB,IAAIiW,IAChC5W,GAAOiyB,EAAMtf,GACfsf,EAAKtf,GAAMhT,KAAKiM,GAEhBiK,GAAeoc,EAAMtf,EAAIhT,KAAKiM,MAIlCm4B,OAAQ,WACN,GAAI9R,IAAQtyB,KAAKmY,QAAUnY,KAAKgB,IAAIiW,IAChCqb,GAAKtyB,KAAKgT,MAAQhT,KAAKiM,KACzBqmB,EAAKtyB,KAAKgT,IAAM,QAKlBma,IACF7pB,KAAM,cAKJmlC,IACFnlC,KAAM,WACJ,GAAI2I,GAAKjM,KAAKiM,EACdjM,MAAKgB,GAAGu1B,MAAM,oBAAqB,WACjCtqB,EAAGgC,gBAAgB,eAYrB0e,IACF7hB,KAAMy0B,GACNv0B,KAAMA,GACN09B,IAAO9G,GACP+G,GAAMtE,GACNM,KAAMA,GACNhB,MAAOA,GACP/0B,GAAIk4B,GACJxjC,KAAM+kC,GACNp8B,GAAIA,GACJkhB,IAAKA,GACLsb,MAAOA,IAGLG,IAEFjU,MAAM,EAENrE,OAAQ,SAAgBtuB,GACjBA,EAEuB,gBAAVA,GAChBhC,KAAKsP,SAAStN,EAAMgH,OAAOmc,MAAM,QAEjCnlB,KAAKsP,SAAS0V,GAAYhjB,IAJ1BhC,KAAK6oC,WAQTv5B,SAAU,SAAkBtN,GAC1BhC,KAAK6oC,QAAQ7mC,EACb,KAAK,GAAIlB,GAAI,EAAG2C,EAAIzB,EAAMjB,OAAQD,EAAI2C,EAAG3C,IAAK,CAC5C,GAAIV,GAAM4B,EAAMlB,EACZV,IACFuD,GAAM3D,KAAKiM,GAAI7L,EAAKuP,IAGxB3P,KAAK8oC,SAAW9mC,GAGlB6mC,QAAS,SAAiB7mC,GACxB,GAAI8mC,GAAW9oC,KAAK8oC,QACpB,IAAKA,EAEL,IADA,GAAIhoC,GAAIgoC,EAAS/nC,OACVD,KAAK,CACV,GAAIX,GAAM2oC,EAAShoC,KACdkB,GAASA,EAAM2D,QAAQxF,GAAO,IACjCwD,GAAM3D,KAAKiM,GAAI9L,EAAK4P,OA+DxBmd,IAEF3C,SAAU+W,GAEVO,QAAS,aAAc,kBAAmB,mBAY1Cv+B,KAAM,WACCtD,KAAKiM,GAAG+W,UAEXhjB,KAAK+oC,UAAY/oC,KAAK6hC,OAAOkH,UACzB/oC,KAAK+oC,YACP/oC,KAAK4K,UAGH5K,KAAK6hC,OAAOmH,iBAEdhpC,KAAKgpC,eAAiB/4B,GAAejQ,KAAKiM,IAAI,IAGhDjM,KAAKipC,mBAAqBjpC,KAAKkpC,UAAY,KAE3ClpC,KAAKmpC,gBAAkB,EACvBnpC,KAAKopC,iBAAmB,KAExBppC,KAAKkR,OAASF,GAAa,eAC3BpO,GAAQ5C,KAAKiM,GAAIjM,KAAKkR,QAKtBlR,KAAKiM,GAAGgC,gBAAgB,MACxBjO,KAAKiM,GAAGgC,gBAAgB,OAEpBjO,KAAKwqB,WAAW2C,KAClBntB,KAAKiM,GAAGgC,gBAAgB,SAAWhL,EAAUjD,KAAKwqB,WAAW2C,MAG3DntB,KAAKstB,SACPttB,KAAKqpC,aAAarpC,KAAK6J,cAY7BymB,OAAQ,SAAgBtuB,GACjBhC,KAAKstB,SACRttB,KAAKqpC,aAAarnC,IAiBtBqnC,aAAc,SAAsBrnC,EAAO8D,GAEzC,GADA9F,KAAKspC,oBACAtnC,EAKE,CACL,GAAI6f,GAAO7hB,IACXA,MAAKupC,iBAAiBvnC,EAAO,WAC3B6f,EAAK2nB,eAAe1jC,SANtB9F,MAAKypC,SAAQ,GACbzpC,KAAKwM,OAAOxM,KAAK0pC,QAAS5jC,GAC1B9F,KAAK0pC,QAAU,MAiBnBH,iBAAkB,SAA0BvnC,EAAO8D,GACjD,GAAI+b,GAAO7hB,IACXA,MAAKipC,mBAAqBpjC,EAAY,SAAUqjC,GAC9CrnB,EAAK8nB,cAAgBT,EAAUx2B,QAAQ3J,OAA0B,gBAAV/G,GAAqBA,EAAQ,MACpF6f,EAAKqnB,UAAYA,EACjBpjC,MAEF9F,KAAKgB,GAAG+yB,kBAAkB/xB,EAAOhC,KAAKipC,qBAYxCO,eAAgB,SAAwB1jC,GAEtC9F,KAAKypC,SAAQ,EACb,IAAI5nB,GAAO7hB,KACP4pC,EAAgB5pC,KAAKkpC,UAAUx2B,QAAQ0qB,SACvCyM,EAAS7pC,KAAK8pC,YACdC,EAAe/pC,KAAKgqC,OACpBJ,KAAkBC,GACpB7pC,KAAKiqC,WAAaF,EAClB3kB,GAAkBwkB,EAAeG,EAAc,WACzCloB,EAAKooB,aAAeF,IAGxBloB,EAAKooB,WAAa,KAClBpoB,EAAKlV,WAAWo9B,EAAcjkC,QAI5B+jC,GACFE,EAAavxB,aAEfxY,KAAK2M,WAAWo9B,EAAcjkC,KAUlCwjC,kBAAmB,WACbtpC,KAAKipC,qBACPjpC,KAAKipC,mBAAmBjjC,SACxBhG,KAAKipC,mBAAqB,OAa9Be,MAAO,SAAeE,GACpB,GAAIL,GAAS7pC,KAAK8pC,WAClB,IAAID,EACF,MAAOA,EAET,IAAI7pC,KAAKkpC,UAAW,CAElB,GAAIx2B,IACF3J,KAAM/I,KAAK2pC,cACX19B,GAAIsG,GAAUvS,KAAKiM,IACnBsU,SAAUvgB,KAAKgpC,eAIft7B,OAAQ1N,KAAKmyB,OAASnyB,KAAKgB,GAG3B4xB,iBAAkB5yB,KAAKgpC,eACvB3W,KAAMryB,KAAKwqB,WAAW2C,IACtB3D,cAAc,EACd2gB,cAAenqC,KAAKmqC,cAIpBjyB,SAAUlY,KAAKgB,GAKfmX,OAAQnY,KAAKmY,OAKbC,MAAOpY,KAAKoY,MAKV8xB,IACFjmC,EAAOyO,EAASw3B,EAElB,IAAI/5B,GAAQ,GAAInQ,MAAKkpC,UAAUx2B,EAM/B,OALI1S,MAAK+oC,YACP/oC,KAAK4K,MAAM5K,KAAKkpC,UAAUhnB,KAAO/R,GAI5BA,IAUX25B,UAAW,WACT,MAAO9pC,MAAK+oC,WAAa/oC,KAAK4K,MAAM5K,KAAKkpC,UAAUhnB,MAUrDunB,QAAS,SAAiBW,GACpBpqC,KAAKiqC,aACFjqC,KAAK+oC,WACR/oC,KAAKiqC,WAAW3W,WAElBtzB,KAAKiqC,WAAa,KAEpB,IAAI95B,GAAQnQ,KAAK0pC,OACjB,QAAKv5B,GAASnQ,KAAK+oC,eACb54B,IAEFA,EAAMk6B,WAAY,EAClBl6B,EAAMqI,YAAW,SAOrBrI,GAAMmjB,UAAS,EAAO8W,IAUxB59B,OAAQ,SAAgB2D,EAAOrK,GAC7B,GAAIijC,GAAY/oC,KAAK+oC,SACrB,IAAI54B,EAAO,CAKTnQ,KAAKmpC,kBACLnpC,KAAKopC,iBAAmBtjC,CACxB,IAAI+b,GAAO7hB,IACXmQ,GAAMkjB,QAAQ,WACZxR,EAAKsnB,kBACAJ,GAAW54B,EAAM8iB,YACjBpR,EAAKsnB,iBAAmBtnB,EAAKunB,mBAChCvnB,EAAKunB,mBACLvnB,EAAKunB,iBAAmB,YAGnBtjC,IACTA,KAYJ6G,WAAY,SAAoBT,EAAQpG,GACtC,GAAI+b,GAAO7hB,KACPs/B,EAAUt/B,KAAK0pC,OAKnB,QAHIpK,IAASA,EAAQ+K,WAAY,GACjCn+B,EAAOm+B,WAAY,EACnBrqC,KAAK0pC,QAAUx9B,EACP2V,EAAKggB,OAAOyI,gBAClB,IAAK,SACHp+B,EAAO6pB,QAAQlU,EAAK3Q,OAAQ,WAC1B2Q,EAAKrV,OAAO8yB,EAASx5B,IAEvB,MACF,KAAK,SACH+b,EAAKrV,OAAO8yB,EAAS,WACnBpzB,EAAO6pB,QAAQlU,EAAK3Q,OAAQpL,IAE9B,MACF,SACE+b,EAAKrV,OAAO8yB,GACZpzB,EAAO6pB,QAAQlU,EAAK3Q,OAAQpL,KAQlCs+B,OAAQ,WAKN,GAJApkC,KAAKspC,oBAELtpC,KAAKypC,UAEDzpC,KAAK4K,MAAO,CACd,IAAK,GAAIzK,KAAOH,MAAK4K,MACnB5K,KAAK4K,MAAMzK,GAAKmzB,UAElBtzB,MAAK4K,MAAQ,QA0Bfkb,GAAmB3b,GAAOoyB,kBAC1B3W,MAGAC,GAAY,sBA2UZ0kB,GAAepgC,GAAOoyB,kBAEtB7V,IAEFpjB,KAAM,WACJ,GAAI6M,GAAQnQ,KAAKgB,GACb0M,EAASyC,EAAM+H,SAEfoM,EAAOtkB,KAAKwqB,WAAWlG,KACvBkmB,EAAWlmB,EAAKrL,KAChBwxB,EAAYnmB,EAAK6B,WACjB/H,EAASkG,EAAKhL,OAASixB,GAAavkB,QAEpC0kB,EAAgB1qC,KAAK0qC,cAAgB,GAAIntB,IAAQ7P,EAAQ+8B,EAAW,SAAUrqC,GAChF+mB,GAAWhX,EAAOmU,EAAMlkB,KAExBge,OAAQA,EACRzV,QAAS2b,EAAK3b,QAGdqT,MAAOhc,KAAKmY,QAOd,IAHAoO,GAASpW,EAAOmU,EAAMomB,EAAc1oC,OAGhCoc,EAAQ,CAGV,GAAIyD,GAAO7hB,IACXmQ,GAAMomB,MAAM,mBAAoB,WAC9B1U,EAAK8oB,aAAe,GAAIptB,IAAQpN,EAAOq6B,EAAU,SAAUpqC,GACzDsqC,EAAczqC,IAAIG,KAKlBw0B,MAAM,QAMdwP,OAAQ,WACNpkC,KAAK0qC,cAAcnX,WACfvzB,KAAK2qC,cACP3qC,KAAK2qC,aAAapX,aAKpBxL,MACA1J,IAAS,EAkCTusB,GAAkB,aAClBC,GAAiB,YACjBC,GAAoB7Q,GAAiB,WACrC8Q,GAAmB7Q,GAAgB,WAiBnC8Q,GAAMxR,IAAaC,OAAOwR,sBAC1BC,GAAyBF,GAE3B,SAAUznC,GACVynC,GAAI,WACFA,GAAIznC,MAEJ,SAAUA,GACZmC,WAAWnC,EAAI,KAkCb4nC,GAAMhjB,GAAWxT,SA2BrBw2B,IAAI3E,MAAQ,SAAU95B,EAAI5G,GACxB9F,KAAKorC,gBACLprC,KAAK0hB,SAAS,eACd1hB,KAAK8F,GAAKA,EACV6J,GAAS3P,KAAKiM,GAAIjM,KAAKooB,YACvB1b,IACA1M,KAAK0oB,SAAU,EACf1oB,KAAKqrC,eAAe,SAChBrrC,KAAK0oB,UAGT1oB,KAAKgG,OAAShG,KAAK6M,OAAS7M,KAAK6M,MAAMy+B,eACvCzjB,GAAQ7nB,KAAKurC,iBASfJ,GAAII,cAAgB,WAClB,GAAI7F,GAAQ1lC,IAGZA,MAAKyoB,aAAc,EACnByiB,GAAuB,WACrBxF,EAAMjd,aAAc,GAEtB,IAAI+iB,GAAYxrC,KAAKwrC,UACjBx3B,EAAOhU,KAAKyrC,qBAAqBzrC,KAAKooB,WACrCpoB,MAAKwoB,YAUCxU,IAAS42B,IAClB76B,GAAY/P,KAAKiM,GAAIjM,KAAKooB,YAVtBpU,IAAS42B,IAEX76B,GAAY/P,KAAKiM,GAAIjM,KAAKooB,YAC1BpoB,KAAK0rC,WAAW5+B,GAAoB0+B,IAC3Bx3B,IAAS62B,GAClB7qC,KAAK0rC,WAAWvR,GAAmBqR,GAEnCA,KAWNL,GAAIK,UAAY,WACdxrC,KAAK0oB,SAAU,EACf1oB,KAAKgG,OAAShG,KAAKwoB,YAAc,KACjCzY,GAAY/P,KAAKiM,GAAIjM,KAAKooB,YAC1BpoB,KAAK0hB,SAAS,cACV1hB,KAAK8F,IAAI9F,KAAK8F,MAwBpBqlC,GAAIQ,MAAQ,SAAUj/B,EAAI5G,GACxB9F,KAAKorC,gBACLprC,KAAK0hB,SAAS,eACd1hB,KAAK0M,GAAKA,EACV1M,KAAK8F,GAAKA,EACV6J,GAAS3P,KAAKiM,GAAIjM,KAAKqoB,YACvBroB,KAAK2oB,MAAO,EACZ3oB,KAAKqrC,eAAe,SAChBrrC,KAAK2oB,OAGT3oB,KAAKgG,OAAShG,KAAK6M,OAAS7M,KAAK6M,MAAM++B,eAKnC5rC,KAAK0M,KAAO1M,KAAKwoB,cAIfxoB,KAAKyoB,YACPzoB,KAAK6rC,YAELhkB,GAAQ7nB,KAAK8rC,kBASnBX,GAAIW,cAAgB,WAClB,GAAI93B,GAAOhU,KAAKyrC,qBAAqBzrC,KAAKqoB,WAC1C,IAAIrU,EAAM,CACR,GAAInF,GAAQmF,IAAS42B,GAAkB99B,GAAqBqtB,EAC5Dn6B,MAAK0rC,WAAW78B,EAAO7O,KAAK6rC,eAE5B7rC,MAAK6rC,aAQTV,GAAIU,UAAY,WACd7rC,KAAK2oB,MAAO,EACZ3oB,KAAKgG,OAAShG,KAAKwoB,YAAc,KACjCxoB,KAAK0M,KACLqD,GAAY/P,KAAKiM,GAAIjM,KAAKqoB,YAC1BroB,KAAK0hB,SAAS,cACV1hB,KAAK8F,IAAI9F,KAAK8F,KAClB9F,KAAK0M,GAAK,MAQZy+B,GAAIC,cAAgB,WAClBprC,KAAK0M,GAAK1M,KAAK8F,GAAK,IACpB,IAAIimC,IAAa,CACb/rC,MAAKuoB,eACPwjB,GAAa,EACb/8B,GAAIhP,KAAKiM,GAAIjM,KAAKsoB,gBAAiBtoB,KAAKuoB,cACxCvoB,KAAKsoB,gBAAkBtoB,KAAKuoB,aAAe,MAEzCvoB,KAAKwoB,cACPujB,GAAa,EACb/rC,KAAKwoB,YAAYxiB,SACjBhG,KAAKwoB,YAAc,MAEjBujB,IACFh8B,GAAY/P,KAAKiM,GAAIjM,KAAKooB,YAC1BrY,GAAY/P,KAAKiM,GAAIjM,KAAKqoB,aAExBroB,KAAKgG,SACPhG,KAAKgG,OAAO1E,KAAKtB,KAAKgB,GAAIhB,KAAKiM,IAC/BjM,KAAKgG,OAAS,OAUlBmlC,GAAIzpB,SAAW,SAAU1N,GACnBhU,KAAK6M,OAAS7M,KAAK6M,MAAMmH,IAC3BhU,KAAK6M,MAAMmH,GAAM1S,KAAKtB,KAAKgB,GAAIhB,KAAKiM,KAexCk/B,GAAIE,eAAiB,SAAUr3B,GAC7B,GAAI4d,GAAO5xB,KAAK6M,OAAS7M,KAAK6M,MAAMmH,EAChC4d,KACEA,EAAK7wB,OAAS,IAChBf,KAAKwoB,YAAc3iB,EAAY7F,KAAKgU,EAAO,UAE7C4d,EAAKtwB,KAAKtB,KAAKgB,GAAIhB,KAAKiM,GAAIjM,KAAKwoB,eAYrC2iB,GAAIM,qBAAuB,SAAUr8B,GAEnC,MAAKtC,IAMLK,SAAS6+B,QAEThsC,KAAK6M,OAAS7M,KAAK6M,MAAMo/B,OAAQ,GAEjCljB,GAAS/oB,KAAKiM,KAVd,CAaA,GAAI+H,GAAOhU,KAAKgU,MAAQhU,KAAK4oB,UAAUxZ,EACvC,IAAI4E,EAAM,MAAOA,EACjB,IAAIk4B,GAAelsC,KAAKiM,GAAG4Y,MACvBsnB,EAAiB1S,OAAO2S,iBAAiBpsC,KAAKiM,IAC9CogC,EAAgBH,EAAapB,KAAsBqB,EAAerB,GACtE,IAAIuB,GAAmC,OAAlBA,EACnBr4B,EAAO42B,OACF,CACL,GAAI0B,GAAeJ,EAAanB,KAAqBoB,EAAepB,GAChEuB,IAAiC,OAAjBA,IAClBt4B,EAAO62B,IAMX,MAHI72B,KACFhU,KAAK4oB,UAAUxZ,GAAa4E,GAEvBA,IAUTm3B,GAAIO,WAAa,SAAU78B,EAAO/I,GAChC9F,KAAKsoB,gBAAkBzZ,CACvB,IAAIgT,GAAO7hB,KACPiM,EAAKjM,KAAKiM,GACVsgC,EAAQvsC,KAAKuoB,aAAe,SAAU9R,GACpCA,EAAEvK,SAAWD,IACf+C,GAAI/C,EAAI4C,EAAO09B,GACf1qB,EAAKyG,gBAAkBzG,EAAK0G,aAAe,MACtC1G,EAAK2G,aAAe1iB,GACvBA,KAIN8I,IAAG3C,EAAI4C,EAAO09B,GAsBhB,IAAIC,KAEFjiB,SAAU6W,GAEV9Q,OAAQ,SAAgBtd,EAAIy5B,GAC1B,GAAIxgC,GAAKjM,KAAKiM,GAEVY,EAAQkG,GAAa/S,KAAKgB,GAAGsX,SAAU,cAAetF,EAC1DA,GAAKA,GAAM,IACXy5B,EAAQA,GAAS,IACjBxgC,EAAGW,UAAY,GAAIub,IAAWlc,EAAI+G,EAAInG,EAAO7M,KAAKgB,IAClD+O,GAAY9D,EAAIwgC,EAAQ,eACxB98B,GAAS1D,EAAI+G,EAAK,iBAIlBoa,IACFvI,MAAOA,GACP6nB,MAAS9D,GACT1b,UAAWA,GACX5I,KAAMoC,GACN/Z,WAAY6/B,IAIV/d,GAAS,cACTC,GAAO,YACPX,GAAY,yBACZD,GAAa,YACbU,GAAe,2BAGf/D,GAAmB,IACnBuD,GAA4B,GAujBhChC,IAAKrC,UAAW,CA4MhB,IAAIuF,IAAgB,aAyMhBuJ,GAAWp0B,OAAO43B,QACrB5Z,QAASA,GACT4I,oBAAqBA,GACrBG,YAAaA,GACbwD,WAAYA,GACZO,aAAcA,KAiPX2B,GAAU,WAkOdiB,IAAUpd,UAAUwV,MAAQ,WAC1B,GAAIphB,GAAO/I,KAAK+I,KACZyhB,EAAaxqB,KAAKwqB,UAGtB,KAAc,UAATzhB,GAAoB/I,KAAKgB,GAAG+L,cAAgB/M,KAAKiM,IAAMjM,KAAKiM,GAAGgC,gBAAiB,CACnF,GAAIwX,GAAO+E,EAAW/E,MAAQ,KAAO1c,CACrC/I,MAAKiM,GAAGgC,gBAAgBwX,GAI1B,GAAIhhB,GAAM+lB,EAAW/lB,GAgBrB,IAfmB,kBAARA,GACTzE,KAAKswB,OAAS7rB,EAEdR,EAAOjE,KAAMyE,GAIfzE,KAAK2sC,eAGD3sC,KAAKsD,MACPtD,KAAKsD,OAEPtD,KAAKiyB,QAAS,EAEVjyB,KAAKstB,QACPttB,KAAKswB,QAAUtwB,KAAKswB,OAAO9F,EAAWlQ,SACjC,KAAKta,KAAK6J,YAAc7J,KAAKqtB,aAAertB,KAAKswB,QAAUtwB,KAAKoe,UAAYpe,KAAK4sC,kBAAmB,CAEzG,GAAIhjC,GAAM5J,IACNA,MAAKswB,OACPtwB,KAAK6sC,QAAU,SAAUzsC,EAAK0sC,GACvBljC,EAAIooB,SACPpoB,EAAI0mB,OAAOlwB,EAAK0sC,IAIpB9sC,KAAK6sC,QAAU/a,EAEjB,IAAI+M,GAAa7+B,KAAKkkC,YAAc5gC,EAAKtD,KAAKkkC,YAAalkC,MAAQ,KAC/D8+B,EAAc9+B,KAAKmkC,aAAe7gC,EAAKtD,KAAKmkC,aAAcnkC,MAAQ,KAClEid,EAAUjd,KAAKghC,SAAW,GAAIzjB,IAAQvd,KAAKgB,GAAIhB,KAAK6J,WAAY7J,KAAK6sC,SAEvElkC,QAAS3I,KAAK2I,QACdyV,OAAQpe,KAAKoe,OACbuW,KAAM30B,KAAK20B,KACXkK,WAAYA,EACZC,YAAaA,EACb9iB,MAAOhc,KAAKmY,QAKVnY,MAAKslC,UACPtlC,KAAKslC,YACItlC,KAAKswB,QACdtwB,KAAKswB,OAAOrT,EAAQjb,SAU1B+vB,GAAUpd,UAAUg4B,aAAe,WACjC,GAAK3sC,KAAK6hC,OAAV,CAGA,GAAIA,GAAS7hC,KAAK6hC,MAElB7hC,MAAK6hC,OAASx9B,OAAOwC,OAAO,KAG5B,KAFA,GACI1G,GAAKC,EAAK2sC,EADVjsC,EAAI+gC,EAAO9gC,OAERD,KACLX,EAAM8C,EAAU4+B,EAAO/gC,IACvBisC,EAAYpqC,EAASxC,GACrBC,EAAM8N,EAAYlO,KAAKiM,GAAI9L,GAChB,MAAPC,EAEFJ,KAAKgtC,mBAAmBD,EAAW3sC,IAGnCA,EAAM0N,EAAQ9N,KAAKiM,GAAI9L,GACZ,MAAPC,IACFJ,KAAK6hC,OAAOkL,GAAqB,KAAR3sC,GAAoBA,MAarD2xB,GAAUpd,UAAUq4B,mBAAqB,SAAU7sC,EAAK0J,GACtD,GAAIgY,GAAO7hB,KACPqlB,GAAS,EACT4nB,GAAWjtC,KAAKmY,QAAUnY,KAAKgB,IAAI0zB,OAAO7qB,EAAY,SAAUzJ,EAAK0sC,GAIvE,GAHAjrB,EAAKggB,OAAO1hC,GAAOC,EAGfilB,EAAQ,CACV,GAAIvf,GAAK+b,EAAKqrB,eAAiBrrB,EAAKqrB,cAAc/sC,EAC9C2F,IACFA,EAAGxE,KAAKugB,EAAMzhB,EAAK0sC,OAGrBznB,IAAS,IAGXwP,WAAW,EACXxX,MAAM,KACJrd,KAAKmtC,mBAAqBntC,KAAKmtC,sBAAwBvkC,KAAKqkC,IAclElb,GAAUpd,UAAUi4B,gBAAkB,WACpC,GAAI/iC,GAAa7J,KAAK6J,UACtB,IAAIA,GAAc7J,KAAK+mC,kBAAoB5qB,GAAatS,GAAa,CACnE,GAAItG,GAAKmX,GAAkB7Q,GAAYF,IACnCqS,EAAQhc,KAAKmY,QAAUnY,KAAKgB,GAC5BwiB,EAAU,SAAiB/M,GAC7BuF,EAAMoxB,OAAS32B,EACflT,EAAGjC,KAAK0a,EAAOA,GACfA,EAAMoxB,OAAS,KAMjB,OAJIptC,MAAK2I,UACP6a,EAAUxH,EAAM0X,cAAclQ,EAAS,KAAMxjB,KAAK2I,UAEpD3I,KAAKswB,OAAO9M,IACL,IAaXuO,GAAUpd,UAAU1U,IAAM,SAAU+B,GAE9BhC,KAAKoe,QACPpe,KAAKm/B,UAAU,WACbn/B,KAAKghC,SAAS/gC,IAAI+B,MAYxB+vB,GAAUpd,UAAUwqB,UAAY,SAAU57B,GACxC,GAAIse,GAAO7hB,IACX6hB,GAAKmQ,SAAU,EACfzuB,EAAGjC,KAAKugB,GACRvE,GAAS,WACPuE,EAAKmQ,SAAU,KAcnBD,GAAUpd,UAAU/F,GAAK,SAAUC,EAAO2U,EAAS1U,GACjDF,GAAG5O,KAAKiM,GAAI4C,EAAO2U,EAAS1U,IAAa9O,KAAKkyB,aAAelyB,KAAKkyB,gBAAkBtpB,MAAMiG,EAAO2U,KAOnGuO,GAAUpd,UAAUqW,UAAY,WAC9B,GAAIhrB,KAAKiyB,OAAQ,CACfjyB,KAAKiyB,QAAS,EACVjyB,KAAKokC,QACPpkC,KAAKokC,SAEHpkC,KAAKghC,UACPhhC,KAAKghC,SAASzN,UAEhB,IACIzyB,GADAusC,EAAYrtC,KAAKkyB,UAErB,IAAImb,EAEF,IADAvsC,EAAIusC,EAAUtsC,OACPD,KACLkO,GAAIhP,KAAKiM,GAAIohC,EAAUvsC,GAAG,GAAIusC,EAAUvsC,GAAG,GAG/C,IAAIwsC,GAAattC,KAAKmtC,gBACtB,IAAIG,EAEF,IADAxsC,EAAIwsC,EAAWvsC,OACRD,KACLwsC,EAAWxsC,IAIfd,MAAKgB,GAAKhB,KAAKiM,GAAKjM,KAAKghC,SAAWhhC,KAAKkyB,WAAa,MAoV1D,IAAI4C,IAAa,YAgoBjBne,IAAU5W,IACVwvB,GAAWxvB,IACX6wB,GAAY7wB,IACZqyB,GAAeryB,IACf0zB,GAAU1zB,IAGVs0B,GAAQt0B,IACRo1B,GAAOp1B,IACPo2B,GAAUp2B,IACVi3B,GAAaj3B,GAEb,IAAIwtC,KAEFhjB,SAAUmX,GACVG,QAAS,QAETv+B,KAAM,WAEJ,GAAIyF,GAAO/I,KAAK6hC,OAAO94B,MAAQ,UAC3BwH,EAAUvQ,KAAKgB,GAAGquB,eAAiBrvB,KAAKgB,GAAGquB,cAActmB,EACxDwH,IAAYA,EAAQC,gBAGvBxQ,KAAKqiB,QAAQ9R,EAAQgC,WAAU,GAAOvS,KAAKgB,GAAGkX,SAAUlY,KAAKgB,IAF7DhB,KAAKwtC,YAMTnrB,QAAS,SAAiB9R,EAASpL,EAAS0b,GAC1C,GAAItQ,GAAWpL,EAAS,CACtB,GAAInF,KAAKiM,GAAGuE,iBAAiD,IAA9BD,EAAQ2Q,WAAWngB,QAAmD,IAAnCwP,EAAQ2Q,WAAW,GAAGtT,UAAkB2C,EAAQ2Q,WAAW,GAAG9S,aAAa,QAAS,CAGpJ,GAAIq/B,GAAYtgC,SAASwD,cAAc,WACvC88B,GAAU/9B,aAAa,SAAU,IACjC+9B,EAAUj7B,UAAYxS,KAAKiM,GAAGuG,UAE9Bi7B,EAAUv1B,SAAWlY,KAAKgB,GAC1BuP,EAAQnE,YAAYqhC,GAEtB,GAAIzxB,GAAQ6E,EAAOA,EAAK1I,OAASnY,KAAKmY,MACtCnY,MAAKihB,OAAS9b,EAAQ+xB,SAAS3mB,EAASsQ,EAAM7E,EAAOhc,KAAKoY,OAExD7H,EACF3N,GAAQ5C,KAAKiM,GAAIsE,GAEjB/D,GAAOxM,KAAKiM,KAIhBuhC,SAAU,WACRxtC,KAAKqiB,QAAQpS,GAAejQ,KAAKiM,IAAI,GAAOjM,KAAKgB,KAGnDojC,OAAQ,WACFpkC,KAAKihB,QACPjhB,KAAKihB,WAKPqI,IAEFiB,SAAUgX,GAEVM,QAAS,QAGTqL,eACEnkC,KAAM,SAAc/G,GAClBqiC,GAAI73B,OAAOlL,KAAKtB,MACZgC,GACFhC,KAAKo1B,OAAOpzB,KAKlBsB,KAAM,WACJtD,KAAKkR,OAASF,GAAa,aAC3BpO,GAAQ5C,KAAKiM,GAAIjM,KAAKkR,QACtBlR,KAAKo1B,OAAOp1B,KAAK6hC,OAAO94B,OAG1BqsB,OAAQ,SAAgBpiB,GACtB,GAAIsW,GAAUvW,GAAa/S,KAAKgB,GAAGsX,SAAU,WAAYtF,GAAI,EACzDsW,KACFtpB,KAAKN,QAAU,GAAIuiB,IAAgBjiB,KAAKgB,GAAIsoB,GAC5C+a,GAAIjP,OAAO9zB,KAAKtB,QAIpBokC,OAAQ,WACFpkC,KAAK+R,MACP/R,KAAK+R,KAAKiQ,YAKZqW,IACFkV,KAAMA,GACNjkB,QAASA,IAGPiO,GAAeqK,GAAKuC,aAiJpBuJ,GAAW,iBAGX/kC,IAEFgvB,QAASA,GACTP,SAAUA,GACVD,QAASA,GAQTwW,MACE7Z,KAAM,SAAc9xB,EAAO4rC,GACzB,MAAwB,gBAAV5rC,GAAqBA,EAAQkE,KAAKC,UAAUnE,EAAO,KAAM0B,UAAU3C,OAAS,EAAI6sC,EAAS,IAEzGha,MAAO,SAAe5xB,GACpB,IACE,MAAOkE,MAAKiT,MAAMnX,GAClB,MAAOyU,GACP,MAAOzU,MASb6rC,WAAY,SAAoB7rC,GAC9B,MAAKA,IAAmB,IAAVA,GACdA,EAAQA,EAAMC,WACPD,EAAM8S,OAAO,GAAG9R,cAAgBhB,EAAMU,MAAM,IAFjB,IASpCorC,UAAW,SAAmB9rC,GAC5B,MAAOA,IAAmB,IAAVA,EAAcA,EAAMC,WAAWe,cAAgB,IAOjE+qC,UAAW,SAAmB/rC,GAC5B,MAAOA,IAAmB,IAAVA,EAAcA,EAAMC,WAAWkB,cAAgB,IAUjE6qC,SAAU,SAAkBhsC,EAAOisC,EAAWC,GAE5C,GADAlsC,EAAQmsC,WAAWnsC,IACdosC,SAASpsC,KAAWA,GAAmB,IAAVA,EAAa,MAAO,EACtDisC,GAAyB,MAAbA,EAAoBA,EAAY,IAC5CC,EAAuB,MAAZA,EAAmBA,EAAW,CACzC,IAAIG,GAAc1rB,KAAK2rB,IAAItsC,GAAOusC,QAAQL,GACtCM,EAAON,EAAWG,EAAY3rC,MAAM,GAAG,EAAKwrC,GAAYG,EACxDvtC,EAAI0tC,EAAKztC,OAAS,EAClB0F,EAAO3F,EAAI,EAAI0tC,EAAK9rC,MAAM,EAAG5B,IAAM0tC,EAAKztC,OAAS,EAAI,IAAM,IAAM,GACjE0tC,EAASP,EAAWG,EAAY3rC,OAAM,EAAKwrC,GAAY,GACvDQ,EAAO1sC,EAAQ,EAAI,IAAM,EAC7B,OAAO0sC,GAAOT,EAAYxnC,EAAO+nC,EAAK9rC,MAAM5B,GAAG8B,QAAQ8qC,GAAU,OAASe,GAgB5EE,UAAW,SAAmB3sC,GAC5B,GAAIkD,GAAOtB,EAAQF,UAAW,GAC1B3C,EAASmE,EAAKnE,MAClB,IAAIA,EAAS,EAAG,CACd,GAAIgG,GAAQ/E,EAAQ,GAAK,CACzB,OAAO+E,KAAS7B,GAAOA,EAAK6B,GAAS7B,EAAKnE,EAAS,GAEnD,MAAOmE,GAAK,IAAgB,IAAVlD,EAAc,GAAK,MAYzC47B,SAAU,SAAkBpa,EAASorB,GACnC,GAAKprB,EAIL,MAHKorB,KACHA,EAAQ,KAEH9pC,EAAU0e,EAASorB,IA6L9B,OAdAzW,IAAiBp4B,IAEjBA,GAAI8uC,QAAU,SAIdnpC,WAAW,WACLyE,GAAO4S,UACLA,IACFA,GAASC,KAAK,OAAQjd,KAGzB,GAEIA","file":"vue.min.js","sourcesContent":["/*!\n * Vue.js v1.0.28\n * (c) 2016 Evan You\n * Released under the MIT License.\n */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n  typeof define === 'function' && define.amd ? define(factory) :\n  (global.Vue = factory());\n}(this, (function () { 'use strict';\n\nfunction set(obj, key, val) {\n  if (hasOwn(obj, key)) {\n    obj[key] = val;\n    return;\n  }\n  if (obj._isVue) {\n    set(obj._data, key, val);\n    return;\n  }\n  var ob = obj.__ob__;\n  if (!ob) {\n    obj[key] = val;\n    return;\n  }\n  ob.convert(key, val);\n  ob.dep.notify();\n  if (ob.vms) {\n    var i = ob.vms.length;\n    while (i--) {\n      var vm = ob.vms[i];\n      vm._proxy(key);\n      vm._digest();\n    }\n  }\n  return val;\n}\n\n/**\n * Delete a property and trigger change if necessary.\n *\n * @param {Object} obj\n * @param {String} key\n */\n\nfunction del(obj, key) {\n  if (!hasOwn(obj, key)) {\n    return;\n  }\n  delete obj[key];\n  var ob = obj.__ob__;\n  if (!ob) {\n    if (obj._isVue) {\n      delete obj._data[key];\n      obj._digest();\n    }\n    return;\n  }\n  ob.dep.notify();\n  if (ob.vms) {\n    var i = ob.vms.length;\n    while (i--) {\n      var vm = ob.vms[i];\n      vm._unproxy(key);\n      vm._digest();\n    }\n  }\n}\n\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\n/**\n * Check whether the object has the property.\n *\n * @param {Object} obj\n * @param {String} key\n * @return {Boolean}\n */\n\nfunction hasOwn(obj, key) {\n  return hasOwnProperty.call(obj, key);\n}\n\n/**\n * Check if an expression is a literal value.\n *\n * @param {String} exp\n * @return {Boolean}\n */\n\nvar literalValueRE = /^\\s?(true|false|-?[\\d\\.]+|'[^']*'|\"[^\"]*\")\\s?$/;\n\nfunction isLiteral(exp) {\n  return literalValueRE.test(exp);\n}\n\n/**\n * Check if a string starts with $ or _\n *\n * @param {String} str\n * @return {Boolean}\n */\n\nfunction isReserved(str) {\n  var c = (str + '').charCodeAt(0);\n  return c === 0x24 || c === 0x5F;\n}\n\n/**\n * Guard text output, make sure undefined outputs\n * empty string\n *\n * @param {*} value\n * @return {String}\n */\n\nfunction _toString(value) {\n  return value == null ? '' : value.toString();\n}\n\n/**\n * Check and convert possible numeric strings to numbers\n * before setting back to data\n *\n * @param {*} value\n * @return {*|Number}\n */\n\nfunction toNumber(value) {\n  if (typeof value !== 'string') {\n    return value;\n  } else {\n    var parsed = Number(value);\n    return isNaN(parsed) ? value : parsed;\n  }\n}\n\n/**\n * Convert string boolean literals into real booleans.\n *\n * @param {*} value\n * @return {*|Boolean}\n */\n\nfunction toBoolean(value) {\n  return value === 'true' ? true : value === 'false' ? false : value;\n}\n\n/**\n * Strip quotes from a string\n *\n * @param {String} str\n * @return {String | false}\n */\n\nfunction stripQuotes(str) {\n  var a = str.charCodeAt(0);\n  var b = str.charCodeAt(str.length - 1);\n  return a === b && (a === 0x22 || a === 0x27) ? str.slice(1, -1) : str;\n}\n\n/**\n * Camelize a hyphen-delimited string.\n *\n * @param {String} str\n * @return {String}\n */\n\nvar camelizeRE = /-(\\w)/g;\n\nfunction camelize(str) {\n  return str.replace(camelizeRE, toUpper);\n}\n\nfunction toUpper(_, c) {\n  return c ? c.toUpperCase() : '';\n}\n\n/**\n * Hyphenate a camelCase string.\n *\n * @param {String} str\n * @return {String}\n */\n\nvar hyphenateRE = /([^-])([A-Z])/g;\n\nfunction hyphenate(str) {\n  return str.replace(hyphenateRE, '$1-$2').replace(hyphenateRE, '$1-$2').toLowerCase();\n}\n\n/**\n * Converts hyphen/underscore/slash delimitered names into\n * camelized classNames.\n *\n * e.g. my-component => MyComponent\n *      some_else    => SomeElse\n *      some/comp    => SomeComp\n *\n * @param {String} str\n * @return {String}\n */\n\nvar classifyRE = /(?:^|[-_\\/])(\\w)/g;\n\nfunction classify(str) {\n  return str.replace(classifyRE, toUpper);\n}\n\n/**\n * Simple bind, faster than native\n *\n * @param {Function} fn\n * @param {Object} ctx\n * @return {Function}\n */\n\nfunction bind(fn, ctx) {\n  return function (a) {\n    var l = arguments.length;\n    return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx);\n  };\n}\n\n/**\n * Convert an Array-like object to a real Array.\n *\n * @param {Array-like} list\n * @param {Number} [start] - start index\n * @return {Array}\n */\n\nfunction toArray(list, start) {\n  start = start || 0;\n  var i = list.length - start;\n  var ret = new Array(i);\n  while (i--) {\n    ret[i] = list[i + start];\n  }\n  return ret;\n}\n\n/**\n * Mix properties into target object.\n *\n * @param {Object} to\n * @param {Object} from\n */\n\nfunction extend(to, from) {\n  var keys = Object.keys(from);\n  var i = keys.length;\n  while (i--) {\n    to[keys[i]] = from[keys[i]];\n  }\n  return to;\n}\n\n/**\n * Quick object check - this is primarily used to tell\n * Objects from primitive values when we know the value\n * is a JSON-compliant type.\n *\n * @param {*} obj\n * @return {Boolean}\n */\n\nfunction isObject(obj) {\n  return obj !== null && typeof obj === 'object';\n}\n\n/**\n * Strict object type check. Only returns true\n * for plain JavaScript objects.\n *\n * @param {*} obj\n * @return {Boolean}\n */\n\nvar toString = Object.prototype.toString;\nvar OBJECT_STRING = '[object Object]';\n\nfunction isPlainObject(obj) {\n  return toString.call(obj) === OBJECT_STRING;\n}\n\n/**\n * Array type check.\n *\n * @param {*} obj\n * @return {Boolean}\n */\n\nvar isArray = Array.isArray;\n\n/**\n * Define a property.\n *\n * @param {Object} obj\n * @param {String} key\n * @param {*} val\n * @param {Boolean} [enumerable]\n */\n\nfunction def(obj, key, val, enumerable) {\n  Object.defineProperty(obj, key, {\n    value: val,\n    enumerable: !!enumerable,\n    writable: true,\n    configurable: true\n  });\n}\n\n/**\n * Debounce a function so it only gets called after the\n * input stops arriving after the given wait period.\n *\n * @param {Function} func\n * @param {Number} wait\n * @return {Function} - the debounced function\n */\n\nfunction _debounce(func, wait) {\n  var timeout, args, context, timestamp, result;\n  var later = function later() {\n    var last = Date.now() - timestamp;\n    if (last < wait && last >= 0) {\n      timeout = setTimeout(later, wait - last);\n    } else {\n      timeout = null;\n      result = func.apply(context, args);\n      if (!timeout) context = args = null;\n    }\n  };\n  return function () {\n    context = this;\n    args = arguments;\n    timestamp = Date.now();\n    if (!timeout) {\n      timeout = setTimeout(later, wait);\n    }\n    return result;\n  };\n}\n\n/**\n * Manual indexOf because it's slightly faster than\n * native.\n *\n * @param {Array} arr\n * @param {*} obj\n */\n\nfunction indexOf(arr, obj) {\n  var i = arr.length;\n  while (i--) {\n    if (arr[i] === obj) return i;\n  }\n  return -1;\n}\n\n/**\n * Make a cancellable version of an async callback.\n *\n * @param {Function} fn\n * @return {Function}\n */\n\nfunction cancellable(fn) {\n  var cb = function cb() {\n    if (!cb.cancelled) {\n      return fn.apply(this, arguments);\n    }\n  };\n  cb.cancel = function () {\n    cb.cancelled = true;\n  };\n  return cb;\n}\n\n/**\n * Check if two values are loosely equal - that is,\n * if they are plain objects, do they have the same shape?\n *\n * @param {*} a\n * @param {*} b\n * @return {Boolean}\n */\n\nfunction looseEqual(a, b) {\n  /* eslint-disable eqeqeq */\n  return a == b || (isObject(a) && isObject(b) ? JSON.stringify(a) === JSON.stringify(b) : false);\n  /* eslint-enable eqeqeq */\n}\n\nvar hasProto = ('__proto__' in {});\n\n// Browser environment sniffing\nvar inBrowser = typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]';\n\n// detect devtools\nvar devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;\n\n// UA sniffing for working around browser-specific quirks\nvar UA = inBrowser && window.navigator.userAgent.toLowerCase();\nvar isIE = UA && UA.indexOf('trident') > 0;\nvar isIE9 = UA && UA.indexOf('msie 9.0') > 0;\nvar isAndroid = UA && UA.indexOf('android') > 0;\nvar isIOS = UA && /iphone|ipad|ipod|ios/.test(UA);\n\nvar transitionProp = undefined;\nvar transitionEndEvent = undefined;\nvar animationProp = undefined;\nvar animationEndEvent = undefined;\n\n// Transition property/event sniffing\nif (inBrowser && !isIE9) {\n  var isWebkitTrans = window.ontransitionend === undefined && window.onwebkittransitionend !== undefined;\n  var isWebkitAnim = window.onanimationend === undefined && window.onwebkitanimationend !== undefined;\n  transitionProp = isWebkitTrans ? 'WebkitTransition' : 'transition';\n  transitionEndEvent = isWebkitTrans ? 'webkitTransitionEnd' : 'transitionend';\n  animationProp = isWebkitAnim ? 'WebkitAnimation' : 'animation';\n  animationEndEvent = isWebkitAnim ? 'webkitAnimationEnd' : 'animationend';\n}\n\n/* istanbul ignore next */\nfunction isNative(Ctor) {\n  return (/native code/.test(Ctor.toString())\n  );\n}\n\n/**\n * Defer a task to execute it asynchronously. Ideally this\n * should be executed as a microtask, so we leverage\n * MutationObserver if it's available, and fallback to\n * setTimeout(0).\n *\n * @param {Function} cb\n * @param {Object} ctx\n */\n\nvar nextTick = (function () {\n  var callbacks = [];\n  var pending = false;\n  var timerFunc = undefined;\n\n  function nextTickHandler() {\n    pending = false;\n    var copies = callbacks.slice(0);\n    callbacks.length = 0;\n    for (var i = 0; i < copies.length; i++) {\n      copies[i]();\n    }\n  }\n\n  // the nextTick behavior leverages the microtask queue, which can be accessed\n  // via either native Promise.then or MutationObserver.\n  // MutationObserver has wider support, however it is seriously bugged in\n  // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It\n  // completely stops working after triggering a few times... so, if native\n  // Promise is available, we will use it:\n  /* istanbul ignore if */\n  if (typeof Promise !== 'undefined' && isNative(Promise)) {\n    var p = Promise.resolve();\n    var noop = function noop() {};\n    timerFunc = function () {\n      p.then(nextTickHandler);\n      // in problematic UIWebViews, Promise.then doesn't completely break, but\n      // it can get stuck in a weird state where callbacks are pushed into the\n      // microtask queue but the queue isn't being flushed, until the browser\n      // needs to do some other work, e.g. handle a timer. Therefore we can\n      // \"force\" the microtask queue to be flushed by adding an empty timer.\n      if (isIOS) setTimeout(noop);\n    };\n  } else if (typeof MutationObserver !== 'undefined') {\n    // use MutationObserver where native Promise is not available,\n    // e.g. IE11, iOS7, Android 4.4\n    var counter = 1;\n    var observer = new MutationObserver(nextTickHandler);\n    var textNode = document.createTextNode(String(counter));\n    observer.observe(textNode, {\n      characterData: true\n    });\n    timerFunc = function () {\n      counter = (counter + 1) % 2;\n      textNode.data = String(counter);\n    };\n  } else {\n    // fallback to setTimeout\n    /* istanbul ignore next */\n    timerFunc = setTimeout;\n  }\n\n  return function (cb, ctx) {\n    var func = ctx ? function () {\n      cb.call(ctx);\n    } : cb;\n    callbacks.push(func);\n    if (pending) return;\n    pending = true;\n    timerFunc(nextTickHandler, 0);\n  };\n})();\n\nvar _Set = undefined;\n/* istanbul ignore if */\nif (typeof Set !== 'undefined' && isNative(Set)) {\n  // use native Set when available.\n  _Set = Set;\n} else {\n  // a non-standard Set polyfill that only works with primitive keys.\n  _Set = function () {\n    this.set = Object.create(null);\n  };\n  _Set.prototype.has = function (key) {\n    return this.set[key] !== undefined;\n  };\n  _Set.prototype.add = function (key) {\n    this.set[key] = 1;\n  };\n  _Set.prototype.clear = function () {\n    this.set = Object.create(null);\n  };\n}\n\nfunction Cache(limit) {\n  this.size = 0;\n  this.limit = limit;\n  this.head = this.tail = undefined;\n  this._keymap = Object.create(null);\n}\n\nvar p = Cache.prototype;\n\n/**\n * Put <value> into the cache associated with <key>.\n * Returns the entry which was removed to make room for\n * the new entry. Otherwise undefined is returned.\n * (i.e. if there was enough room already).\n *\n * @param {String} key\n * @param {*} value\n * @return {Entry|undefined}\n */\n\np.put = function (key, value) {\n  var removed;\n\n  var entry = this.get(key, true);\n  if (!entry) {\n    if (this.size === this.limit) {\n      removed = this.shift();\n    }\n    entry = {\n      key: key\n    };\n    this._keymap[key] = entry;\n    if (this.tail) {\n      this.tail.newer = entry;\n      entry.older = this.tail;\n    } else {\n      this.head = entry;\n    }\n    this.tail = entry;\n    this.size++;\n  }\n  entry.value = value;\n\n  return removed;\n};\n\n/**\n * Purge the least recently used (oldest) entry from the\n * cache. Returns the removed entry or undefined if the\n * cache was empty.\n */\n\np.shift = function () {\n  var entry = this.head;\n  if (entry) {\n    this.head = this.head.newer;\n    this.head.older = undefined;\n    entry.newer = entry.older = undefined;\n    this._keymap[entry.key] = undefined;\n    this.size--;\n  }\n  return entry;\n};\n\n/**\n * Get and register recent use of <key>. Returns the value\n * associated with <key> or undefined if not in cache.\n *\n * @param {String} key\n * @param {Boolean} returnEntry\n * @return {Entry|*}\n */\n\np.get = function (key, returnEntry) {\n  var entry = this._keymap[key];\n  if (entry === undefined) return;\n  if (entry === this.tail) {\n    return returnEntry ? entry : entry.value;\n  }\n  // HEAD--------------TAIL\n  //   <.older   .newer>\n  //  <--- add direction --\n  //   A  B  C  <D>  E\n  if (entry.newer) {\n    if (entry === this.head) {\n      this.head = entry.newer;\n    }\n    entry.newer.older = entry.older; // C <-- E.\n  }\n  if (entry.older) {\n    entry.older.newer = entry.newer; // C. --> E\n  }\n  entry.newer = undefined; // D --x\n  entry.older = this.tail; // D. --> E\n  if (this.tail) {\n    this.tail.newer = entry; // E. <-- D\n  }\n  this.tail = entry;\n  return returnEntry ? entry : entry.value;\n};\n\nvar cache$1 = new Cache(1000);\nvar reservedArgRE = /^in$|^-?\\d+/;\n\n/**\n * Parser state\n */\n\nvar str;\nvar dir;\nvar len;\nvar index;\nvar chr;\nvar state;\nvar startState = 0;\nvar filterState = 1;\nvar filterNameState = 2;\nvar filterArgState = 3;\n\nvar doubleChr = 0x22;\nvar singleChr = 0x27;\nvar pipeChr = 0x7C;\nvar escapeChr = 0x5C;\nvar spaceChr = 0x20;\n\nvar expStartChr = { 0x5B: 1, 0x7B: 1, 0x28: 1 };\nvar expChrPair = { 0x5B: 0x5D, 0x7B: 0x7D, 0x28: 0x29 };\n\nfunction peek() {\n  return str.charCodeAt(index + 1);\n}\n\nfunction next() {\n  return str.charCodeAt(++index);\n}\n\nfunction eof() {\n  return index >= len;\n}\n\nfunction eatSpace() {\n  while (peek() === spaceChr) {\n    next();\n  }\n}\n\nfunction isStringStart(chr) {\n  return chr === doubleChr || chr === singleChr;\n}\n\nfunction isExpStart(chr) {\n  return expStartChr[chr];\n}\n\nfunction isExpEnd(start, chr) {\n  return expChrPair[start] === chr;\n}\n\nfunction parseString() {\n  var stringQuote = next();\n  var chr;\n  while (!eof()) {\n    chr = next();\n    // escape char\n    if (chr === escapeChr) {\n      next();\n    } else if (chr === stringQuote) {\n      break;\n    }\n  }\n}\n\nfunction parseSpecialExp(chr) {\n  var inExp = 0;\n  var startChr = chr;\n\n  while (!eof()) {\n    chr = peek();\n    if (isStringStart(chr)) {\n      parseString();\n      continue;\n    }\n\n    if (startChr === chr) {\n      inExp++;\n    }\n    if (isExpEnd(startChr, chr)) {\n      inExp--;\n    }\n\n    next();\n\n    if (inExp === 0) {\n      break;\n    }\n  }\n}\n\n/**\n * syntax:\n * expression | filterName  [arg  arg [| filterName arg arg]]\n */\n\nfunction parseExpression() {\n  var start = index;\n  while (!eof()) {\n    chr = peek();\n    if (isStringStart(chr)) {\n      parseString();\n    } else if (isExpStart(chr)) {\n      parseSpecialExp(chr);\n    } else if (chr === pipeChr) {\n      next();\n      chr = peek();\n      if (chr === pipeChr) {\n        next();\n      } else {\n        if (state === startState || state === filterArgState) {\n          state = filterState;\n        }\n        break;\n      }\n    } else if (chr === spaceChr && (state === filterNameState || state === filterArgState)) {\n      eatSpace();\n      break;\n    } else {\n      if (state === filterState) {\n        state = filterNameState;\n      }\n      next();\n    }\n  }\n\n  return str.slice(start + 1, index) || null;\n}\n\nfunction parseFilterList() {\n  var filters = [];\n  while (!eof()) {\n    filters.push(parseFilter());\n  }\n  return filters;\n}\n\nfunction parseFilter() {\n  var filter = {};\n  var args;\n\n  state = filterState;\n  filter.name = parseExpression().trim();\n\n  state = filterArgState;\n  args = parseFilterArguments();\n\n  if (args.length) {\n    filter.args = args;\n  }\n  return filter;\n}\n\nfunction parseFilterArguments() {\n  var args = [];\n  while (!eof() && state !== filterState) {\n    var arg = parseExpression();\n    if (!arg) {\n      break;\n    }\n    args.push(processFilterArg(arg));\n  }\n\n  return args;\n}\n\n/**\n * Check if an argument is dynamic and strip quotes.\n *\n * @param {String} arg\n * @return {Object}\n */\n\nfunction processFilterArg(arg) {\n  if (reservedArgRE.test(arg)) {\n    return {\n      value: toNumber(arg),\n      dynamic: false\n    };\n  } else {\n    var stripped = stripQuotes(arg);\n    var dynamic = stripped === arg;\n    return {\n      value: dynamic ? arg : stripped,\n      dynamic: dynamic\n    };\n  }\n}\n\n/**\n * Parse a directive value and extract the expression\n * and its filters into a descriptor.\n *\n * Example:\n *\n * \"a + 1 | uppercase\" will yield:\n * {\n *   expression: 'a + 1',\n *   filters: [\n *     { name: 'uppercase', args: null }\n *   ]\n * }\n *\n * @param {String} s\n * @return {Object}\n */\n\nfunction parseDirective(s) {\n  var hit = cache$1.get(s);\n  if (hit) {\n    return hit;\n  }\n\n  // reset parser state\n  str = s;\n  dir = {};\n  len = str.length;\n  index = -1;\n  chr = '';\n  state = startState;\n\n  var filters;\n\n  if (str.indexOf('|') < 0) {\n    dir.expression = str.trim();\n  } else {\n    dir.expression = parseExpression().trim();\n    filters = parseFilterList();\n    if (filters.length) {\n      dir.filters = filters;\n    }\n  }\n\n  cache$1.put(s, dir);\n  return dir;\n}\n\nvar directive = Object.freeze({\n  parseDirective: parseDirective\n});\n\nvar regexEscapeRE = /[-.*+?^${}()|[\\]\\/\\\\]/g;\nvar cache = undefined;\nvar tagRE = undefined;\nvar htmlRE = undefined;\n/**\n * Escape a string so it can be used in a RegExp\n * constructor.\n *\n * @param {String} str\n */\n\nfunction escapeRegex(str) {\n  return str.replace(regexEscapeRE, '\\\\$&');\n}\n\nfunction compileRegex() {\n  var open = escapeRegex(config.delimiters[0]);\n  var close = escapeRegex(config.delimiters[1]);\n  var unsafeOpen = escapeRegex(config.unsafeDelimiters[0]);\n  var unsafeClose = escapeRegex(config.unsafeDelimiters[1]);\n  tagRE = new RegExp(unsafeOpen + '((?:.|\\\\n)+?)' + unsafeClose + '|' + open + '((?:.|\\\\n)+?)' + close, 'g');\n  htmlRE = new RegExp('^' + unsafeOpen + '((?:.|\\\\n)+?)' + unsafeClose + '$');\n  // reset cache\n  cache = new Cache(1000);\n}\n\n/**\n * Parse a template text string into an array of tokens.\n *\n * @param {String} text\n * @return {Array<Object> | null}\n *               - {String} type\n *               - {String} value\n *               - {Boolean} [html]\n *               - {Boolean} [oneTime]\n */\n\nfunction parseText(text) {\n  if (!cache) {\n    compileRegex();\n  }\n  var hit = cache.get(text);\n  if (hit) {\n    return hit;\n  }\n  if (!tagRE.test(text)) {\n    return null;\n  }\n  var tokens = [];\n  var lastIndex = tagRE.lastIndex = 0;\n  var match, index, html, value, first, oneTime;\n  /* eslint-disable no-cond-assign */\n  while (match = tagRE.exec(text)) {\n    /* eslint-enable no-cond-assign */\n    index = match.index;\n    // push text token\n    if (index > lastIndex) {\n      tokens.push({\n        value: text.slice(lastIndex, index)\n      });\n    }\n    // tag token\n    html = htmlRE.test(match[0]);\n    value = html ? match[1] : match[2];\n    first = value.charCodeAt(0);\n    oneTime = first === 42; // *\n    value = oneTime ? value.slice(1) : value;\n    tokens.push({\n      tag: true,\n      value: value.trim(),\n      html: html,\n      oneTime: oneTime\n    });\n    lastIndex = index + match[0].length;\n  }\n  if (lastIndex < text.length) {\n    tokens.push({\n      value: text.slice(lastIndex)\n    });\n  }\n  cache.put(text, tokens);\n  return tokens;\n}\n\n/**\n * Format a list of tokens into an expression.\n * e.g. tokens parsed from 'a {{b}} c' can be serialized\n * into one single expression as '\"a \" + b + \" c\"'.\n *\n * @param {Array} tokens\n * @param {Vue} [vm]\n * @return {String}\n */\n\nfunction tokensToExp(tokens, vm) {\n  if (tokens.length > 1) {\n    return tokens.map(function (token) {\n      return formatToken(token, vm);\n    }).join('+');\n  } else {\n    return formatToken(tokens[0], vm, true);\n  }\n}\n\n/**\n * Format a single token.\n *\n * @param {Object} token\n * @param {Vue} [vm]\n * @param {Boolean} [single]\n * @return {String}\n */\n\nfunction formatToken(token, vm, single) {\n  return token.tag ? token.oneTime && vm ? '\"' + vm.$eval(token.value) + '\"' : inlineFilters(token.value, single) : '\"' + token.value + '\"';\n}\n\n/**\n * For an attribute with multiple interpolation tags,\n * e.g. attr=\"some-{{thing | filter}}\", in order to combine\n * the whole thing into a single watchable expression, we\n * have to inline those filters. This function does exactly\n * that. This is a bit hacky but it avoids heavy changes\n * to directive parser and watcher mechanism.\n *\n * @param {String} exp\n * @param {Boolean} single\n * @return {String}\n */\n\nvar filterRE = /[^|]\\|[^|]/;\nfunction inlineFilters(exp, single) {\n  if (!filterRE.test(exp)) {\n    return single ? exp : '(' + exp + ')';\n  } else {\n    var dir = parseDirective(exp);\n    if (!dir.filters) {\n      return '(' + exp + ')';\n    } else {\n      return 'this._applyFilters(' + dir.expression + // value\n      ',null,' + // oldValue (null for read)\n      JSON.stringify(dir.filters) + // filter descriptors\n      ',false)'; // write?\n    }\n  }\n}\n\nvar text = Object.freeze({\n  compileRegex: compileRegex,\n  parseText: parseText,\n  tokensToExp: tokensToExp\n});\n\nvar delimiters = ['{{', '}}'];\nvar unsafeDelimiters = ['{{{', '}}}'];\n\nvar config = Object.defineProperties({\n\n  /**\n   * Whether to print debug messages.\n   * Also enables stack trace for warnings.\n   *\n   * @type {Boolean}\n   */\n\n  debug: false,\n\n  /**\n   * Whether to suppress warnings.\n   *\n   * @type {Boolean}\n   */\n\n  silent: false,\n\n  /**\n   * Whether to use async rendering.\n   */\n\n  async: true,\n\n  /**\n   * Whether to warn against errors caught when evaluating\n   * expressions.\n   */\n\n  warnExpressionErrors: true,\n\n  /**\n   * Whether to allow devtools inspection.\n   * Disabled by default in production builds.\n   */\n\n  devtools: 'production' !== 'production',\n\n  /**\n   * Internal flag to indicate the delimiters have been\n   * changed.\n   *\n   * @type {Boolean}\n   */\n\n  _delimitersChanged: true,\n\n  /**\n   * List of asset types that a component can own.\n   *\n   * @type {Array}\n   */\n\n  _assetTypes: ['component', 'directive', 'elementDirective', 'filter', 'transition', 'partial'],\n\n  /**\n   * prop binding modes\n   */\n\n  _propBindingModes: {\n    ONE_WAY: 0,\n    TWO_WAY: 1,\n    ONE_TIME: 2\n  },\n\n  /**\n   * Max circular updates allowed in a batcher flush cycle.\n   */\n\n  _maxUpdateCount: 100\n\n}, {\n  delimiters: { /**\n                 * Interpolation delimiters. Changing these would trigger\n                 * the text parser to re-compile the regular expressions.\n                 *\n                 * @type {Array<String>}\n                 */\n\n    get: function get() {\n      return delimiters;\n    },\n    set: function set(val) {\n      delimiters = val;\n      compileRegex();\n    },\n    configurable: true,\n    enumerable: true\n  },\n  unsafeDelimiters: {\n    get: function get() {\n      return unsafeDelimiters;\n    },\n    set: function set(val) {\n      unsafeDelimiters = val;\n      compileRegex();\n    },\n    configurable: true,\n    enumerable: true\n  }\n});\n\nvar warn = undefined;\n\n/**\n * Append with transition.\n *\n * @param {Element} el\n * @param {Element} target\n * @param {Vue} vm\n * @param {Function} [cb]\n */\n\nfunction appendWithTransition(el, target, vm, cb) {\n  applyTransition(el, 1, function () {\n    target.appendChild(el);\n  }, vm, cb);\n}\n\n/**\n * InsertBefore with transition.\n *\n * @param {Element} el\n * @param {Element} target\n * @param {Vue} vm\n * @param {Function} [cb]\n */\n\nfunction beforeWithTransition(el, target, vm, cb) {\n  applyTransition(el, 1, function () {\n    before(el, target);\n  }, vm, cb);\n}\n\n/**\n * Remove with transition.\n *\n * @param {Element} el\n * @param {Vue} vm\n * @param {Function} [cb]\n */\n\nfunction removeWithTransition(el, vm, cb) {\n  applyTransition(el, -1, function () {\n    remove(el);\n  }, vm, cb);\n}\n\n/**\n * Apply transitions with an operation callback.\n *\n * @param {Element} el\n * @param {Number} direction\n *                  1: enter\n *                 -1: leave\n * @param {Function} op - the actual DOM operation\n * @param {Vue} vm\n * @param {Function} [cb]\n */\n\nfunction applyTransition(el, direction, op, vm, cb) {\n  var transition = el.__v_trans;\n  if (!transition ||\n  // skip if there are no js hooks and CSS transition is\n  // not supported\n  !transition.hooks && !transitionEndEvent ||\n  // skip transitions for initial compile\n  !vm._isCompiled ||\n  // if the vm is being manipulated by a parent directive\n  // during the parent's compilation phase, skip the\n  // animation.\n  vm.$parent && !vm.$parent._isCompiled) {\n    op();\n    if (cb) cb();\n    return;\n  }\n  var action = direction > 0 ? 'enter' : 'leave';\n  transition[action](op, cb);\n}\n\nvar transition = Object.freeze({\n  appendWithTransition: appendWithTransition,\n  beforeWithTransition: beforeWithTransition,\n  removeWithTransition: removeWithTransition,\n  applyTransition: applyTransition\n});\n\n/**\n * Query an element selector if it's not an element already.\n *\n * @param {String|Element} el\n * @return {Element}\n */\n\nfunction query(el) {\n  if (typeof el === 'string') {\n    var selector = el;\n    el = document.querySelector(el);\n    if (!el) {\n      'production' !== 'production' && warn('Cannot find element: ' + selector);\n    }\n  }\n  return el;\n}\n\n/**\n * Check if a node is in the document.\n * Note: document.documentElement.contains should work here\n * but always returns false for comment nodes in phantomjs,\n * making unit tests difficult. This is fixed by doing the\n * contains() check on the node's parentNode instead of\n * the node itself.\n *\n * @param {Node} node\n * @return {Boolean}\n */\n\nfunction inDoc(node) {\n  if (!node) return false;\n  var doc = node.ownerDocument.documentElement;\n  var parent = node.parentNode;\n  return doc === node || doc === parent || !!(parent && parent.nodeType === 1 && doc.contains(parent));\n}\n\n/**\n * Get and remove an attribute from a node.\n *\n * @param {Node} node\n * @param {String} _attr\n */\n\nfunction getAttr(node, _attr) {\n  var val = node.getAttribute(_attr);\n  if (val !== null) {\n    node.removeAttribute(_attr);\n  }\n  return val;\n}\n\n/**\n * Get an attribute with colon or v-bind: prefix.\n *\n * @param {Node} node\n * @param {String} name\n * @return {String|null}\n */\n\nfunction getBindAttr(node, name) {\n  var val = getAttr(node, ':' + name);\n  if (val === null) {\n    val = getAttr(node, 'v-bind:' + name);\n  }\n  return val;\n}\n\n/**\n * Check the presence of a bind attribute.\n *\n * @param {Node} node\n * @param {String} name\n * @return {Boolean}\n */\n\nfunction hasBindAttr(node, name) {\n  return node.hasAttribute(name) || node.hasAttribute(':' + name) || node.hasAttribute('v-bind:' + name);\n}\n\n/**\n * Insert el before target\n *\n * @param {Element} el\n * @param {Element} target\n */\n\nfunction before(el, target) {\n  target.parentNode.insertBefore(el, target);\n}\n\n/**\n * Insert el after target\n *\n * @param {Element} el\n * @param {Element} target\n */\n\nfunction after(el, target) {\n  if (target.nextSibling) {\n    before(el, target.nextSibling);\n  } else {\n    target.parentNode.appendChild(el);\n  }\n}\n\n/**\n * Remove el from DOM\n *\n * @param {Element} el\n */\n\nfunction remove(el) {\n  el.parentNode.removeChild(el);\n}\n\n/**\n * Prepend el to target\n *\n * @param {Element} el\n * @param {Element} target\n */\n\nfunction prepend(el, target) {\n  if (target.firstChild) {\n    before(el, target.firstChild);\n  } else {\n    target.appendChild(el);\n  }\n}\n\n/**\n * Replace target with el\n *\n * @param {Element} target\n * @param {Element} el\n */\n\nfunction replace(target, el) {\n  var parent = target.parentNode;\n  if (parent) {\n    parent.replaceChild(el, target);\n  }\n}\n\n/**\n * Add event listener shorthand.\n *\n * @param {Element} el\n * @param {String} event\n * @param {Function} cb\n * @param {Boolean} [useCapture]\n */\n\nfunction on(el, event, cb, useCapture) {\n  el.addEventListener(event, cb, useCapture);\n}\n\n/**\n * Remove event listener shorthand.\n *\n * @param {Element} el\n * @param {String} event\n * @param {Function} cb\n */\n\nfunction off(el, event, cb) {\n  el.removeEventListener(event, cb);\n}\n\n/**\n * For IE9 compat: when both class and :class are present\n * getAttribute('class') returns wrong value...\n *\n * @param {Element} el\n * @return {String}\n */\n\nfunction getClass(el) {\n  var classname = el.className;\n  if (typeof classname === 'object') {\n    classname = classname.baseVal || '';\n  }\n  return classname;\n}\n\n/**\n * In IE9, setAttribute('class') will result in empty class\n * if the element also has the :class attribute; However in\n * PhantomJS, setting `className` does not work on SVG elements...\n * So we have to do a conditional check here.\n *\n * @param {Element} el\n * @param {String} cls\n */\n\nfunction setClass(el, cls) {\n  /* istanbul ignore if */\n  if (isIE9 && !/svg$/.test(el.namespaceURI)) {\n    el.className = cls;\n  } else {\n    el.setAttribute('class', cls);\n  }\n}\n\n/**\n * Add class with compatibility for IE & SVG\n *\n * @param {Element} el\n * @param {String} cls\n */\n\nfunction addClass(el, cls) {\n  if (el.classList) {\n    el.classList.add(cls);\n  } else {\n    var cur = ' ' + getClass(el) + ' ';\n    if (cur.indexOf(' ' + cls + ' ') < 0) {\n      setClass(el, (cur + cls).trim());\n    }\n  }\n}\n\n/**\n * Remove class with compatibility for IE & SVG\n *\n * @param {Element} el\n * @param {String} cls\n */\n\nfunction removeClass(el, cls) {\n  if (el.classList) {\n    el.classList.remove(cls);\n  } else {\n    var cur = ' ' + getClass(el) + ' ';\n    var tar = ' ' + cls + ' ';\n    while (cur.indexOf(tar) >= 0) {\n      cur = cur.replace(tar, ' ');\n    }\n    setClass(el, cur.trim());\n  }\n  if (!el.className) {\n    el.removeAttribute('class');\n  }\n}\n\n/**\n * Extract raw content inside an element into a temporary\n * container div\n *\n * @param {Element} el\n * @param {Boolean} asFragment\n * @return {Element|DocumentFragment}\n */\n\nfunction extractContent(el, asFragment) {\n  var child;\n  var rawContent;\n  /* istanbul ignore if */\n  if (isTemplate(el) && isFragment(el.content)) {\n    el = el.content;\n  }\n  if (el.hasChildNodes()) {\n    trimNode(el);\n    rawContent = asFragment ? document.createDocumentFragment() : document.createElement('div');\n    /* eslint-disable no-cond-assign */\n    while (child = el.firstChild) {\n      /* eslint-enable no-cond-assign */\n      rawContent.appendChild(child);\n    }\n  }\n  return rawContent;\n}\n\n/**\n * Trim possible empty head/tail text and comment\n * nodes inside a parent.\n *\n * @param {Node} node\n */\n\nfunction trimNode(node) {\n  var child;\n  /* eslint-disable no-sequences */\n  while ((child = node.firstChild, isTrimmable(child))) {\n    node.removeChild(child);\n  }\n  while ((child = node.lastChild, isTrimmable(child))) {\n    node.removeChild(child);\n  }\n  /* eslint-enable no-sequences */\n}\n\nfunction isTrimmable(node) {\n  return node && (node.nodeType === 3 && !node.data.trim() || node.nodeType === 8);\n}\n\n/**\n * Check if an element is a template tag.\n * Note if the template appears inside an SVG its tagName\n * will be in lowercase.\n *\n * @param {Element} el\n */\n\nfunction isTemplate(el) {\n  return el.tagName && el.tagName.toLowerCase() === 'template';\n}\n\n/**\n * Create an \"anchor\" for performing dom insertion/removals.\n * This is used in a number of scenarios:\n * - fragment instance\n * - v-html\n * - v-if\n * - v-for\n * - component\n *\n * @param {String} content\n * @param {Boolean} persist - IE trashes empty textNodes on\n *                            cloneNode(true), so in certain\n *                            cases the anchor needs to be\n *                            non-empty to be persisted in\n *                            templates.\n * @return {Comment|Text}\n */\n\nfunction createAnchor(content, persist) {\n  var anchor = config.debug ? document.createComment(content) : document.createTextNode(persist ? ' ' : '');\n  anchor.__v_anchor = true;\n  return anchor;\n}\n\n/**\n * Find a component ref attribute that starts with $.\n *\n * @param {Element} node\n * @return {String|undefined}\n */\n\nvar refRE = /^v-ref:/;\n\nfunction findRef(node) {\n  if (node.hasAttributes()) {\n    var attrs = node.attributes;\n    for (var i = 0, l = attrs.length; i < l; i++) {\n      var name = attrs[i].name;\n      if (refRE.test(name)) {\n        return camelize(name.replace(refRE, ''));\n      }\n    }\n  }\n}\n\n/**\n * Map a function to a range of nodes .\n *\n * @param {Node} node\n * @param {Node} end\n * @param {Function} op\n */\n\nfunction mapNodeRange(node, end, op) {\n  var next;\n  while (node !== end) {\n    next = node.nextSibling;\n    op(node);\n    node = next;\n  }\n  op(end);\n}\n\n/**\n * Remove a range of nodes with transition, store\n * the nodes in a fragment with correct ordering,\n * and call callback when done.\n *\n * @param {Node} start\n * @param {Node} end\n * @param {Vue} vm\n * @param {DocumentFragment} frag\n * @param {Function} cb\n */\n\nfunction removeNodeRange(start, end, vm, frag, cb) {\n  var done = false;\n  var removed = 0;\n  var nodes = [];\n  mapNodeRange(start, end, function (node) {\n    if (node === end) done = true;\n    nodes.push(node);\n    removeWithTransition(node, vm, onRemoved);\n  });\n  function onRemoved() {\n    removed++;\n    if (done && removed >= nodes.length) {\n      for (var i = 0; i < nodes.length; i++) {\n        frag.appendChild(nodes[i]);\n      }\n      cb && cb();\n    }\n  }\n}\n\n/**\n * Check if a node is a DocumentFragment.\n *\n * @param {Node} node\n * @return {Boolean}\n */\n\nfunction isFragment(node) {\n  return node && node.nodeType === 11;\n}\n\n/**\n * Get outerHTML of elements, taking care\n * of SVG elements in IE as well.\n *\n * @param {Element} el\n * @return {String}\n */\n\nfunction getOuterHTML(el) {\n  if (el.outerHTML) {\n    return el.outerHTML;\n  } else {\n    var container = document.createElement('div');\n    container.appendChild(el.cloneNode(true));\n    return container.innerHTML;\n  }\n}\n\nvar commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/i;\nvar reservedTagRE = /^(slot|partial|component)$/i;\n\n/**\n * Check if an element is a component, if yes return its\n * component id.\n *\n * @param {Element} el\n * @param {Object} options\n * @return {Object|undefined}\n */\n\nfunction checkComponentAttr(el, options) {\n  var tag = el.tagName.toLowerCase();\n  var hasAttrs = el.hasAttributes();\n  if (!commonTagRE.test(tag) && !reservedTagRE.test(tag)) {\n    if (resolveAsset(options, 'components', tag)) {\n      return { id: tag };\n    } else {\n      var is = hasAttrs && getIsBinding(el, options);\n      if (is) {\n        return is;\n      } else if ('production' !== 'production') {}\n    }\n  } else if (hasAttrs) {\n    return getIsBinding(el, options);\n  }\n}\n\n/**\n * Get \"is\" binding from an element.\n *\n * @param {Element} el\n * @param {Object} options\n * @return {Object|undefined}\n */\n\nfunction getIsBinding(el, options) {\n  // dynamic syntax\n  var exp = el.getAttribute('is');\n  if (exp != null) {\n    if (resolveAsset(options, 'components', exp)) {\n      el.removeAttribute('is');\n      return { id: exp };\n    }\n  } else {\n    exp = getBindAttr(el, 'is');\n    if (exp != null) {\n      return { id: exp, dynamic: true };\n    }\n  }\n}\n\n/**\n * Option overwriting strategies are functions that handle\n * how to merge a parent option value and a child option\n * value into the final value.\n *\n * All strategy functions follow the same signature:\n *\n * @param {*} parentVal\n * @param {*} childVal\n * @param {Vue} [vm]\n */\n\nvar strats = config.optionMergeStrategies = Object.create(null);\n\n/**\n * Helper that recursively merges two data objects together.\n */\n\nfunction mergeData(to, from) {\n  var key, toVal, fromVal;\n  for (key in from) {\n    toVal = to[key];\n    fromVal = from[key];\n    if (!hasOwn(to, key)) {\n      set(to, key, fromVal);\n    } else if (isObject(toVal) && isObject(fromVal)) {\n      mergeData(toVal, fromVal);\n    }\n  }\n  return to;\n}\n\n/**\n * Data\n */\n\nstrats.data = function (parentVal, childVal, vm) {\n  if (!vm) {\n    // in a Vue.extend merge, both should be functions\n    if (!childVal) {\n      return parentVal;\n    }\n    if (typeof childVal !== 'function') {\n      'production' !== 'production' && warn('The \"data\" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm);\n      return parentVal;\n    }\n    if (!parentVal) {\n      return childVal;\n    }\n    // when parentVal & childVal are both present,\n    // we need to return a function that returns the\n    // merged result of both functions... no need to\n    // check if parentVal is a function here because\n    // it has to be a function to pass previous merges.\n    return function mergedDataFn() {\n      return mergeData(childVal.call(this), parentVal.call(this));\n    };\n  } else if (parentVal || childVal) {\n    return function mergedInstanceDataFn() {\n      // instance merge\n      var instanceData = typeof childVal === 'function' ? childVal.call(vm) : childVal;\n      var defaultData = typeof parentVal === 'function' ? parentVal.call(vm) : undefined;\n      if (instanceData) {\n        return mergeData(instanceData, defaultData);\n      } else {\n        return defaultData;\n      }\n    };\n  }\n};\n\n/**\n * El\n */\n\nstrats.el = function (parentVal, childVal, vm) {\n  if (!vm && childVal && typeof childVal !== 'function') {\n    'production' !== 'production' && warn('The \"el\" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm);\n    return;\n  }\n  var ret = childVal || parentVal;\n  // invoke the element factory if this is instance merge\n  return vm && typeof ret === 'function' ? ret.call(vm) : ret;\n};\n\n/**\n * Hooks and param attributes are merged as arrays.\n */\n\nstrats.init = strats.created = strats.ready = strats.attached = strats.detached = strats.beforeCompile = strats.compiled = strats.beforeDestroy = strats.destroyed = strats.activate = function (parentVal, childVal) {\n  return childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [childVal] : parentVal;\n};\n\n/**\n * Assets\n *\n * When a vm is present (instance creation), we need to do\n * a three-way merge between constructor options, instance\n * options and parent options.\n */\n\nfunction mergeAssets(parentVal, childVal) {\n  var res = Object.create(parentVal || null);\n  return childVal ? extend(res, guardArrayAssets(childVal)) : res;\n}\n\nconfig._assetTypes.forEach(function (type) {\n  strats[type + 's'] = mergeAssets;\n});\n\n/**\n * Events & Watchers.\n *\n * Events & watchers hashes should not overwrite one\n * another, so we merge them as arrays.\n */\n\nstrats.watch = strats.events = function (parentVal, childVal) {\n  if (!childVal) return parentVal;\n  if (!parentVal) return childVal;\n  var ret = {};\n  extend(ret, parentVal);\n  for (var key in childVal) {\n    var parent = ret[key];\n    var child = childVal[key];\n    if (parent && !isArray(parent)) {\n      parent = [parent];\n    }\n    ret[key] = parent ? parent.concat(child) : [child];\n  }\n  return ret;\n};\n\n/**\n * Other object hashes.\n */\n\nstrats.props = strats.methods = strats.computed = function (parentVal, childVal) {\n  if (!childVal) return parentVal;\n  if (!parentVal) return childVal;\n  var ret = Object.create(null);\n  extend(ret, parentVal);\n  extend(ret, childVal);\n  return ret;\n};\n\n/**\n * Default strategy.\n */\n\nvar defaultStrat = function defaultStrat(parentVal, childVal) {\n  return childVal === undefined ? parentVal : childVal;\n};\n\n/**\n * Make sure component options get converted to actual\n * constructors.\n *\n * @param {Object} options\n */\n\nfunction guardComponents(options) {\n  if (options.components) {\n    var components = options.components = guardArrayAssets(options.components);\n    var ids = Object.keys(components);\n    var def;\n    if ('production' !== 'production') {}\n    for (var i = 0, l = ids.length; i < l; i++) {\n      var key = ids[i];\n      if (commonTagRE.test(key) || reservedTagRE.test(key)) {\n        'production' !== 'production' && warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + key);\n        continue;\n      }\n      // record a all lowercase <-> kebab-case mapping for\n      // possible custom element case error warning\n      if ('production' !== 'production') {}\n      def = components[key];\n      if (isPlainObject(def)) {\n        components[key] = Vue.extend(def);\n      }\n    }\n  }\n}\n\n/**\n * Ensure all props option syntax are normalized into the\n * Object-based format.\n *\n * @param {Object} options\n */\n\nfunction guardProps(options) {\n  var props = options.props;\n  var i, val;\n  if (isArray(props)) {\n    options.props = {};\n    i = props.length;\n    while (i--) {\n      val = props[i];\n      if (typeof val === 'string') {\n        options.props[val] = null;\n      } else if (val.name) {\n        options.props[val.name] = val;\n      }\n    }\n  } else if (isPlainObject(props)) {\n    var keys = Object.keys(props);\n    i = keys.length;\n    while (i--) {\n      val = props[keys[i]];\n      if (typeof val === 'function') {\n        props[keys[i]] = { type: val };\n      }\n    }\n  }\n}\n\n/**\n * Guard an Array-format assets option and converted it\n * into the key-value Object format.\n *\n * @param {Object|Array} assets\n * @return {Object}\n */\n\nfunction guardArrayAssets(assets) {\n  if (isArray(assets)) {\n    var res = {};\n    var i = assets.length;\n    var asset;\n    while (i--) {\n      asset = assets[i];\n      var id = typeof asset === 'function' ? asset.options && asset.options.name || asset.id : asset.name || asset.id;\n      if (!id) {\n        'production' !== 'production' && warn('Array-syntax assets must provide a \"name\" or \"id\" field.');\n      } else {\n        res[id] = asset;\n      }\n    }\n    return res;\n  }\n  return assets;\n}\n\n/**\n * Merge two option objects into a new one.\n * Core utility used in both instantiation and inheritance.\n *\n * @param {Object} parent\n * @param {Object} child\n * @param {Vue} [vm] - if vm is present, indicates this is\n *                     an instantiation merge.\n */\n\nfunction mergeOptions(parent, child, vm) {\n  guardComponents(child);\n  guardProps(child);\n  if ('production' !== 'production') {}\n  var options = {};\n  var key;\n  if (child['extends']) {\n    parent = typeof child['extends'] === 'function' ? mergeOptions(parent, child['extends'].options, vm) : mergeOptions(parent, child['extends'], vm);\n  }\n  if (child.mixins) {\n    for (var i = 0, l = child.mixins.length; i < l; i++) {\n      var mixin = child.mixins[i];\n      var mixinOptions = mixin.prototype instanceof Vue ? mixin.options : mixin;\n      parent = mergeOptions(parent, mixinOptions, vm);\n    }\n  }\n  for (key in parent) {\n    mergeField(key);\n  }\n  for (key in child) {\n    if (!hasOwn(parent, key)) {\n      mergeField(key);\n    }\n  }\n  function mergeField(key) {\n    var strat = strats[key] || defaultStrat;\n    options[key] = strat(parent[key], child[key], vm, key);\n  }\n  return options;\n}\n\n/**\n * Resolve an asset.\n * This function is used because child instances need access\n * to assets defined in its ancestor chain.\n *\n * @param {Object} options\n * @param {String} type\n * @param {String} id\n * @param {Boolean} warnMissing\n * @return {Object|Function}\n */\n\nfunction resolveAsset(options, type, id, warnMissing) {\n  /* istanbul ignore if */\n  if (typeof id !== 'string') {\n    return;\n  }\n  var assets = options[type];\n  var camelizedId;\n  var res = assets[id] ||\n  // camelCase ID\n  assets[camelizedId = camelize(id)] ||\n  // Pascal Case ID\n  assets[camelizedId.charAt(0).toUpperCase() + camelizedId.slice(1)];\n  if ('production' !== 'production' && warnMissing && !res) {}\n  return res;\n}\n\nvar uid$1 = 0;\n\n/**\n * A dep is an observable that can have multiple\n * directives subscribing to it.\n *\n * @constructor\n */\nfunction Dep() {\n  this.id = uid$1++;\n  this.subs = [];\n}\n\n// the current target watcher being evaluated.\n// this is globally unique because there could be only one\n// watcher being evaluated at any time.\nDep.target = null;\n\n/**\n * Add a directive subscriber.\n *\n * @param {Directive} sub\n */\n\nDep.prototype.addSub = function (sub) {\n  this.subs.push(sub);\n};\n\n/**\n * Remove a directive subscriber.\n *\n * @param {Directive} sub\n */\n\nDep.prototype.removeSub = function (sub) {\n  this.subs.$remove(sub);\n};\n\n/**\n * Add self as a dependency to the target watcher.\n */\n\nDep.prototype.depend = function () {\n  Dep.target.addDep(this);\n};\n\n/**\n * Notify all subscribers of a new value.\n */\n\nDep.prototype.notify = function () {\n  // stablize the subscriber list first\n  var subs = toArray(this.subs);\n  for (var i = 0, l = subs.length; i < l; i++) {\n    subs[i].update();\n  }\n};\n\nvar arrayProto = Array.prototype;\nvar arrayMethods = Object.create(arrayProto)\n\n/**\n * Intercept mutating methods and emit events\n */\n\n;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {\n  // cache original method\n  var original = arrayProto[method];\n  def(arrayMethods, method, function mutator() {\n    // avoid leaking arguments:\n    // http://jsperf.com/closure-with-arguments\n    var i = arguments.length;\n    var args = new Array(i);\n    while (i--) {\n      args[i] = arguments[i];\n    }\n    var result = original.apply(this, args);\n    var ob = this.__ob__;\n    var inserted;\n    switch (method) {\n      case 'push':\n        inserted = args;\n        break;\n      case 'unshift':\n        inserted = args;\n        break;\n      case 'splice':\n        inserted = args.slice(2);\n        break;\n    }\n    if (inserted) ob.observeArray(inserted);\n    // notify change\n    ob.dep.notify();\n    return result;\n  });\n});\n\n/**\n * Swap the element at the given index with a new value\n * and emits corresponding event.\n *\n * @param {Number} index\n * @param {*} val\n * @return {*} - replaced element\n */\n\ndef(arrayProto, '$set', function $set(index, val) {\n  if (index >= this.length) {\n    this.length = Number(index) + 1;\n  }\n  return this.splice(index, 1, val)[0];\n});\n\n/**\n * Convenience method to remove the element at given index or target element reference.\n *\n * @param {*} item\n */\n\ndef(arrayProto, '$remove', function $remove(item) {\n  /* istanbul ignore if */\n  if (!this.length) return;\n  var index = indexOf(this, item);\n  if (index > -1) {\n    return this.splice(index, 1);\n  }\n});\n\nvar arrayKeys = Object.getOwnPropertyNames(arrayMethods);\n\n/**\n * By default, when a reactive property is set, the new value is\n * also converted to become reactive. However in certain cases, e.g.\n * v-for scope alias and props, we don't want to force conversion\n * because the value may be a nested value under a frozen data structure.\n *\n * So whenever we want to set a reactive property without forcing\n * conversion on the new value, we wrap that call inside this function.\n */\n\nvar shouldConvert = true;\n\nfunction withoutConversion(fn) {\n  shouldConvert = false;\n  fn();\n  shouldConvert = true;\n}\n\n/**\n * Observer class that are attached to each observed\n * object. Once attached, the observer converts target\n * object's property keys into getter/setters that\n * collect dependencies and dispatches updates.\n *\n * @param {Array|Object} value\n * @constructor\n */\n\nfunction Observer(value) {\n  this.value = value;\n  this.dep = new Dep();\n  def(value, '__ob__', this);\n  if (isArray(value)) {\n    var augment = hasProto ? protoAugment : copyAugment;\n    augment(value, arrayMethods, arrayKeys);\n    this.observeArray(value);\n  } else {\n    this.walk(value);\n  }\n}\n\n// Instance methods\n\n/**\n * Walk through each property and convert them into\n * getter/setters. This method should only be called when\n * value type is Object.\n *\n * @param {Object} obj\n */\n\nObserver.prototype.walk = function (obj) {\n  var keys = Object.keys(obj);\n  for (var i = 0, l = keys.length; i < l; i++) {\n    this.convert(keys[i], obj[keys[i]]);\n  }\n};\n\n/**\n * Observe a list of Array items.\n *\n * @param {Array} items\n */\n\nObserver.prototype.observeArray = function (items) {\n  for (var i = 0, l = items.length; i < l; i++) {\n    observe(items[i]);\n  }\n};\n\n/**\n * Convert a property into getter/setter so we can emit\n * the events when the property is accessed/changed.\n *\n * @param {String} key\n * @param {*} val\n */\n\nObserver.prototype.convert = function (key, val) {\n  defineReactive(this.value, key, val);\n};\n\n/**\n * Add an owner vm, so that when $set/$delete mutations\n * happen we can notify owner vms to proxy the keys and\n * digest the watchers. This is only called when the object\n * is observed as an instance's root $data.\n *\n * @param {Vue} vm\n */\n\nObserver.prototype.addVm = function (vm) {\n  (this.vms || (this.vms = [])).push(vm);\n};\n\n/**\n * Remove an owner vm. This is called when the object is\n * swapped out as an instance's $data object.\n *\n * @param {Vue} vm\n */\n\nObserver.prototype.removeVm = function (vm) {\n  this.vms.$remove(vm);\n};\n\n// helpers\n\n/**\n * Augment an target Object or Array by intercepting\n * the prototype chain using __proto__\n *\n * @param {Object|Array} target\n * @param {Object} src\n */\n\nfunction protoAugment(target, src) {\n  /* eslint-disable no-proto */\n  target.__proto__ = src;\n  /* eslint-enable no-proto */\n}\n\n/**\n * Augment an target Object or Array by defining\n * hidden properties.\n *\n * @param {Object|Array} target\n * @param {Object} proto\n */\n\nfunction copyAugment(target, src, keys) {\n  for (var i = 0, l = keys.length; i < l; i++) {\n    var key = keys[i];\n    def(target, key, src[key]);\n  }\n}\n\n/**\n * Attempt to create an observer instance for a value,\n * returns the new observer if successfully observed,\n * or the existing observer if the value already has one.\n *\n * @param {*} value\n * @param {Vue} [vm]\n * @return {Observer|undefined}\n * @static\n */\n\nfunction observe(value, vm) {\n  if (!value || typeof value !== 'object') {\n    return;\n  }\n  var ob;\n  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {\n    ob = value.__ob__;\n  } else if (shouldConvert && (isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue) {\n    ob = new Observer(value);\n  }\n  if (ob && vm) {\n    ob.addVm(vm);\n  }\n  return ob;\n}\n\n/**\n * Define a reactive property on an Object.\n *\n * @param {Object} obj\n * @param {String} key\n * @param {*} val\n */\n\nfunction defineReactive(obj, key, val) {\n  var dep = new Dep();\n\n  var property = Object.getOwnPropertyDescriptor(obj, key);\n  if (property && property.configurable === false) {\n    return;\n  }\n\n  // cater for pre-defined getter/setters\n  var getter = property && property.get;\n  var setter = property && property.set;\n\n  var childOb = observe(val);\n  Object.defineProperty(obj, key, {\n    enumerable: true,\n    configurable: true,\n    get: function reactiveGetter() {\n      var value = getter ? getter.call(obj) : val;\n      if (Dep.target) {\n        dep.depend();\n        if (childOb) {\n          childOb.dep.depend();\n        }\n        if (isArray(value)) {\n          for (var e, i = 0, l = value.length; i < l; i++) {\n            e = value[i];\n            e && e.__ob__ && e.__ob__.dep.depend();\n          }\n        }\n      }\n      return value;\n    },\n    set: function reactiveSetter(newVal) {\n      var value = getter ? getter.call(obj) : val;\n      if (newVal === value) {\n        return;\n      }\n      if (setter) {\n        setter.call(obj, newVal);\n      } else {\n        val = newVal;\n      }\n      childOb = observe(newVal);\n      dep.notify();\n    }\n  });\n}\n\n\n\nvar util = Object.freeze({\n\tdefineReactive: defineReactive,\n\tset: set,\n\tdel: del,\n\thasOwn: hasOwn,\n\tisLiteral: isLiteral,\n\tisReserved: isReserved,\n\t_toString: _toString,\n\ttoNumber: toNumber,\n\ttoBoolean: toBoolean,\n\tstripQuotes: stripQuotes,\n\tcamelize: camelize,\n\thyphenate: hyphenate,\n\tclassify: classify,\n\tbind: bind,\n\ttoArray: toArray,\n\textend: extend,\n\tisObject: isObject,\n\tisPlainObject: isPlainObject,\n\tdef: def,\n\tdebounce: _debounce,\n\tindexOf: indexOf,\n\tcancellable: cancellable,\n\tlooseEqual: looseEqual,\n\tisArray: isArray,\n\thasProto: hasProto,\n\tinBrowser: inBrowser,\n\tdevtools: devtools,\n\tisIE: isIE,\n\tisIE9: isIE9,\n\tisAndroid: isAndroid,\n\tisIOS: isIOS,\n\tget transitionProp () { return transitionProp; },\n\tget transitionEndEvent () { return transitionEndEvent; },\n\tget animationProp () { return animationProp; },\n\tget animationEndEvent () { return animationEndEvent; },\n\tnextTick: nextTick,\n\tget _Set () { return _Set; },\n\tquery: query,\n\tinDoc: inDoc,\n\tgetAttr: getAttr,\n\tgetBindAttr: getBindAttr,\n\thasBindAttr: hasBindAttr,\n\tbefore: before,\n\tafter: after,\n\tremove: remove,\n\tprepend: prepend,\n\treplace: replace,\n\ton: on,\n\toff: off,\n\tsetClass: setClass,\n\taddClass: addClass,\n\tremoveClass: removeClass,\n\textractContent: extractContent,\n\ttrimNode: trimNode,\n\tisTemplate: isTemplate,\n\tcreateAnchor: createAnchor,\n\tfindRef: findRef,\n\tmapNodeRange: mapNodeRange,\n\tremoveNodeRange: removeNodeRange,\n\tisFragment: isFragment,\n\tgetOuterHTML: getOuterHTML,\n\tmergeOptions: mergeOptions,\n\tresolveAsset: resolveAsset,\n\tcheckComponentAttr: checkComponentAttr,\n\tcommonTagRE: commonTagRE,\n\treservedTagRE: reservedTagRE,\n\twarn: warn\n});\n\nvar uid = 0;\n\nfunction initMixin (Vue) {\n  /**\n   * The main init sequence. This is called for every\n   * instance, including ones that are created from extended\n   * constructors.\n   *\n   * @param {Object} options - this options object should be\n   *                           the result of merging class\n   *                           options and the options passed\n   *                           in to the constructor.\n   */\n\n  Vue.prototype._init = function (options) {\n    options = options || {};\n\n    this.$el = null;\n    this.$parent = options.parent;\n    this.$root = this.$parent ? this.$parent.$root : this;\n    this.$children = [];\n    this.$refs = {}; // child vm references\n    this.$els = {}; // element references\n    this._watchers = []; // all watchers as an array\n    this._directives = []; // all directives\n\n    // a uid\n    this._uid = uid++;\n\n    // a flag to avoid this being observed\n    this._isVue = true;\n\n    // events bookkeeping\n    this._events = {}; // registered callbacks\n    this._eventsCount = {}; // for $broadcast optimization\n\n    // fragment instance properties\n    this._isFragment = false;\n    this._fragment = // @type {DocumentFragment}\n    this._fragmentStart = // @type {Text|Comment}\n    this._fragmentEnd = null; // @type {Text|Comment}\n\n    // lifecycle state\n    this._isCompiled = this._isDestroyed = this._isReady = this._isAttached = this._isBeingDestroyed = this._vForRemoving = false;\n    this._unlinkFn = null;\n\n    // context:\n    // if this is a transcluded component, context\n    // will be the common parent vm of this instance\n    // and its host.\n    this._context = options._context || this.$parent;\n\n    // scope:\n    // if this is inside an inline v-for, the scope\n    // will be the intermediate scope created for this\n    // repeat fragment. this is used for linking props\n    // and container directives.\n    this._scope = options._scope;\n\n    // fragment:\n    // if this instance is compiled inside a Fragment, it\n    // needs to register itself as a child of that fragment\n    // for attach/detach to work properly.\n    this._frag = options._frag;\n    if (this._frag) {\n      this._frag.children.push(this);\n    }\n\n    // push self into parent / transclusion host\n    if (this.$parent) {\n      this.$parent.$children.push(this);\n    }\n\n    // merge options.\n    options = this.$options = mergeOptions(this.constructor.options, options, this);\n\n    // set ref\n    this._updateRef();\n\n    // initialize data as empty object.\n    // it will be filled up in _initData().\n    this._data = {};\n\n    // call init hook\n    this._callHook('init');\n\n    // initialize data observation and scope inheritance.\n    this._initState();\n\n    // setup event system and option events.\n    this._initEvents();\n\n    // call created hook\n    this._callHook('created');\n\n    // if `el` option is passed, start compilation.\n    if (options.el) {\n      this.$mount(options.el);\n    }\n  };\n}\n\nvar pathCache = new Cache(1000);\n\n// actions\nvar APPEND = 0;\nvar PUSH = 1;\nvar INC_SUB_PATH_DEPTH = 2;\nvar PUSH_SUB_PATH = 3;\n\n// states\nvar BEFORE_PATH = 0;\nvar IN_PATH = 1;\nvar BEFORE_IDENT = 2;\nvar IN_IDENT = 3;\nvar IN_SUB_PATH = 4;\nvar IN_SINGLE_QUOTE = 5;\nvar IN_DOUBLE_QUOTE = 6;\nvar AFTER_PATH = 7;\nvar ERROR = 8;\n\nvar pathStateMachine = [];\n\npathStateMachine[BEFORE_PATH] = {\n  'ws': [BEFORE_PATH],\n  'ident': [IN_IDENT, APPEND],\n  '[': [IN_SUB_PATH],\n  'eof': [AFTER_PATH]\n};\n\npathStateMachine[IN_PATH] = {\n  'ws': [IN_PATH],\n  '.': [BEFORE_IDENT],\n  '[': [IN_SUB_PATH],\n  'eof': [AFTER_PATH]\n};\n\npathStateMachine[BEFORE_IDENT] = {\n  'ws': [BEFORE_IDENT],\n  'ident': [IN_IDENT, APPEND]\n};\n\npathStateMachine[IN_IDENT] = {\n  'ident': [IN_IDENT, APPEND],\n  '0': [IN_IDENT, APPEND],\n  'number': [IN_IDENT, APPEND],\n  'ws': [IN_PATH, PUSH],\n  '.': [BEFORE_IDENT, PUSH],\n  '[': [IN_SUB_PATH, PUSH],\n  'eof': [AFTER_PATH, PUSH]\n};\n\npathStateMachine[IN_SUB_PATH] = {\n  \"'\": [IN_SINGLE_QUOTE, APPEND],\n  '\"': [IN_DOUBLE_QUOTE, APPEND],\n  '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],\n  ']': [IN_PATH, PUSH_SUB_PATH],\n  'eof': ERROR,\n  'else': [IN_SUB_PATH, APPEND]\n};\n\npathStateMachine[IN_SINGLE_QUOTE] = {\n  \"'\": [IN_SUB_PATH, APPEND],\n  'eof': ERROR,\n  'else': [IN_SINGLE_QUOTE, APPEND]\n};\n\npathStateMachine[IN_DOUBLE_QUOTE] = {\n  '\"': [IN_SUB_PATH, APPEND],\n  'eof': ERROR,\n  'else': [IN_DOUBLE_QUOTE, APPEND]\n};\n\n/**\n * Determine the type of a character in a keypath.\n *\n * @param {Char} ch\n * @return {String} type\n */\n\nfunction getPathCharType(ch) {\n  if (ch === undefined) {\n    return 'eof';\n  }\n\n  var code = ch.charCodeAt(0);\n\n  switch (code) {\n    case 0x5B: // [\n    case 0x5D: // ]\n    case 0x2E: // .\n    case 0x22: // \"\n    case 0x27: // '\n    case 0x30:\n      // 0\n      return ch;\n\n    case 0x5F: // _\n    case 0x24:\n      // $\n      return 'ident';\n\n    case 0x20: // Space\n    case 0x09: // Tab\n    case 0x0A: // Newline\n    case 0x0D: // Return\n    case 0xA0: // No-break space\n    case 0xFEFF: // Byte Order Mark\n    case 0x2028: // Line Separator\n    case 0x2029:\n      // Paragraph Separator\n      return 'ws';\n  }\n\n  // a-z, A-Z\n  if (code >= 0x61 && code <= 0x7A || code >= 0x41 && code <= 0x5A) {\n    return 'ident';\n  }\n\n  // 1-9\n  if (code >= 0x31 && code <= 0x39) {\n    return 'number';\n  }\n\n  return 'else';\n}\n\n/**\n * Format a subPath, return its plain form if it is\n * a literal string or number. Otherwise prepend the\n * dynamic indicator (*).\n *\n * @param {String} path\n * @return {String}\n */\n\nfunction formatSubPath(path) {\n  var trimmed = path.trim();\n  // invalid leading 0\n  if (path.charAt(0) === '0' && isNaN(path)) {\n    return false;\n  }\n  return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed;\n}\n\n/**\n * Parse a string path into an array of segments\n *\n * @param {String} path\n * @return {Array|undefined}\n */\n\nfunction parse(path) {\n  var keys = [];\n  var index = -1;\n  var mode = BEFORE_PATH;\n  var subPathDepth = 0;\n  var c, newChar, key, type, transition, action, typeMap;\n\n  var actions = [];\n\n  actions[PUSH] = function () {\n    if (key !== undefined) {\n      keys.push(key);\n      key = undefined;\n    }\n  };\n\n  actions[APPEND] = function () {\n    if (key === undefined) {\n      key = newChar;\n    } else {\n      key += newChar;\n    }\n  };\n\n  actions[INC_SUB_PATH_DEPTH] = function () {\n    actions[APPEND]();\n    subPathDepth++;\n  };\n\n  actions[PUSH_SUB_PATH] = function () {\n    if (subPathDepth > 0) {\n      subPathDepth--;\n      mode = IN_SUB_PATH;\n      actions[APPEND]();\n    } else {\n      subPathDepth = 0;\n      key = formatSubPath(key);\n      if (key === false) {\n        return false;\n      } else {\n        actions[PUSH]();\n      }\n    }\n  };\n\n  function maybeUnescapeQuote() {\n    var nextChar = path[index + 1];\n    if (mode === IN_SINGLE_QUOTE && nextChar === \"'\" || mode === IN_DOUBLE_QUOTE && nextChar === '\"') {\n      index++;\n      newChar = '\\\\' + nextChar;\n      actions[APPEND]();\n      return true;\n    }\n  }\n\n  while (mode != null) {\n    index++;\n    c = path[index];\n\n    if (c === '\\\\' && maybeUnescapeQuote()) {\n      continue;\n    }\n\n    type = getPathCharType(c);\n    typeMap = pathStateMachine[mode];\n    transition = typeMap[type] || typeMap['else'] || ERROR;\n\n    if (transition === ERROR) {\n      return; // parse error\n    }\n\n    mode = transition[0];\n    action = actions[transition[1]];\n    if (action) {\n      newChar = transition[2];\n      newChar = newChar === undefined ? c : newChar;\n      if (action() === false) {\n        return;\n      }\n    }\n\n    if (mode === AFTER_PATH) {\n      keys.raw = path;\n      return keys;\n    }\n  }\n}\n\n/**\n * External parse that check for a cache hit first\n *\n * @param {String} path\n * @return {Array|undefined}\n */\n\nfunction parsePath(path) {\n  var hit = pathCache.get(path);\n  if (!hit) {\n    hit = parse(path);\n    if (hit) {\n      pathCache.put(path, hit);\n    }\n  }\n  return hit;\n}\n\n/**\n * Get from an object from a path string\n *\n * @param {Object} obj\n * @param {String} path\n */\n\nfunction getPath(obj, path) {\n  return parseExpression$1(path).get(obj);\n}\n\n/**\n * Set on an object from a path\n *\n * @param {Object} obj\n * @param {String | Array} path\n * @param {*} val\n */\n\nfunction setPath(obj, path, val) {\n  var original = obj;\n  if (typeof path === 'string') {\n    path = parse(path);\n  }\n  if (!path || !isObject(obj)) {\n    return false;\n  }\n  var last, key;\n  for (var i = 0, l = path.length; i < l; i++) {\n    last = obj;\n    key = path[i];\n    if (key.charAt(0) === '*') {\n      key = parseExpression$1(key.slice(1)).get.call(original, original);\n    }\n    if (i < l - 1) {\n      obj = obj[key];\n      if (!isObject(obj)) {\n        obj = {};\n        if ('production' !== 'production' && last._isVue) {}\n        set(last, key, obj);\n      }\n    } else {\n      if (isArray(obj)) {\n        obj.$set(key, val);\n      } else if (key in obj) {\n        obj[key] = val;\n      } else {\n        if ('production' !== 'production' && obj._isVue) {}\n        set(obj, key, val);\n      }\n    }\n  }\n  return true;\n}\n\nvar path = Object.freeze({\n  parsePath: parsePath,\n  getPath: getPath,\n  setPath: setPath\n});\n\nvar expressionCache = new Cache(1000);\n\nvar allowedKeywords = 'Math,Date,this,true,false,null,undefined,Infinity,NaN,' + 'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,' + 'encodeURIComponent,parseInt,parseFloat';\nvar allowedKeywordsRE = new RegExp('^(' + allowedKeywords.replace(/,/g, '\\\\b|') + '\\\\b)');\n\n// keywords that don't make sense inside expressions\nvar improperKeywords = 'break,case,class,catch,const,continue,debugger,default,' + 'delete,do,else,export,extends,finally,for,function,if,' + 'import,in,instanceof,let,return,super,switch,throw,try,' + 'var,while,with,yield,enum,await,implements,package,' + 'protected,static,interface,private,public';\nvar improperKeywordsRE = new RegExp('^(' + improperKeywords.replace(/,/g, '\\\\b|') + '\\\\b)');\n\nvar wsRE = /\\s/g;\nvar newlineRE = /\\n/g;\nvar saveRE = /[\\{,]\\s*[\\w\\$_]+\\s*:|('(?:[^'\\\\]|\\\\.)*'|\"(?:[^\"\\\\]|\\\\.)*\"|`(?:[^`\\\\]|\\\\.)*\\$\\{|\\}(?:[^`\\\\\"']|\\\\.)*`|`(?:[^`\\\\]|\\\\.)*`)|new |typeof |void /g;\nvar restoreRE = /\"(\\d+)\"/g;\nvar pathTestRE = /^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*|\\['.*?'\\]|\\[\".*?\"\\]|\\[\\d+\\]|\\[[A-Za-z_$][\\w$]*\\])*$/;\nvar identRE = /[^\\w$\\.](?:[A-Za-z_$][\\w$]*)/g;\nvar literalValueRE$1 = /^(?:true|false|null|undefined|Infinity|NaN)$/;\n\nfunction noop() {}\n\n/**\n * Save / Rewrite / Restore\n *\n * When rewriting paths found in an expression, it is\n * possible for the same letter sequences to be found in\n * strings and Object literal property keys. Therefore we\n * remove and store these parts in a temporary array, and\n * restore them after the path rewrite.\n */\n\nvar saved = [];\n\n/**\n * Save replacer\n *\n * The save regex can match two possible cases:\n * 1. An opening object literal\n * 2. A string\n * If matched as a plain string, we need to escape its\n * newlines, since the string needs to be preserved when\n * generating the function body.\n *\n * @param {String} str\n * @param {String} isString - str if matched as a string\n * @return {String} - placeholder with index\n */\n\nfunction save(str, isString) {\n  var i = saved.length;\n  saved[i] = isString ? str.replace(newlineRE, '\\\\n') : str;\n  return '\"' + i + '\"';\n}\n\n/**\n * Path rewrite replacer\n *\n * @param {String} raw\n * @return {String}\n */\n\nfunction rewrite(raw) {\n  var c = raw.charAt(0);\n  var path = raw.slice(1);\n  if (allowedKeywordsRE.test(path)) {\n    return raw;\n  } else {\n    path = path.indexOf('\"') > -1 ? path.replace(restoreRE, restore) : path;\n    return c + 'scope.' + path;\n  }\n}\n\n/**\n * Restore replacer\n *\n * @param {String} str\n * @param {String} i - matched save index\n * @return {String}\n */\n\nfunction restore(str, i) {\n  return saved[i];\n}\n\n/**\n * Rewrite an expression, prefixing all path accessors with\n * `scope.` and generate getter/setter functions.\n *\n * @param {String} exp\n * @return {Function}\n */\n\nfunction compileGetter(exp) {\n  if (improperKeywordsRE.test(exp)) {\n    'production' !== 'production' && warn('Avoid using reserved keywords in expression: ' + exp);\n  }\n  // reset state\n  saved.length = 0;\n  // save strings and object literal keys\n  var body = exp.replace(saveRE, save).replace(wsRE, '');\n  // rewrite all paths\n  // pad 1 space here because the regex matches 1 extra char\n  body = (' ' + body).replace(identRE, rewrite).replace(restoreRE, restore);\n  return makeGetterFn(body);\n}\n\n/**\n * Build a getter function. Requires eval.\n *\n * We isolate the try/catch so it doesn't affect the\n * optimization of the parse function when it is not called.\n *\n * @param {String} body\n * @return {Function|undefined}\n */\n\nfunction makeGetterFn(body) {\n  try {\n    /* eslint-disable no-new-func */\n    return new Function('scope', 'return ' + body + ';');\n    /* eslint-enable no-new-func */\n  } catch (e) {\n    if ('production' !== 'production') {}\n    return noop;\n  }\n}\n\n/**\n * Compile a setter function for the expression.\n *\n * @param {String} exp\n * @return {Function|undefined}\n */\n\nfunction compileSetter(exp) {\n  var path = parsePath(exp);\n  if (path) {\n    return function (scope, val) {\n      setPath(scope, path, val);\n    };\n  } else {\n    'production' !== 'production' && warn('Invalid setter expression: ' + exp);\n  }\n}\n\n/**\n * Parse an expression into re-written getter/setters.\n *\n * @param {String} exp\n * @param {Boolean} needSet\n * @return {Function}\n */\n\nfunction parseExpression$1(exp, needSet) {\n  exp = exp.trim();\n  // try cache\n  var hit = expressionCache.get(exp);\n  if (hit) {\n    if (needSet && !hit.set) {\n      hit.set = compileSetter(hit.exp);\n    }\n    return hit;\n  }\n  var res = { exp: exp };\n  res.get = isSimplePath(exp) && exp.indexOf('[') < 0\n  // optimized super simple getter\n  ? makeGetterFn('scope.' + exp)\n  // dynamic getter\n  : compileGetter(exp);\n  if (needSet) {\n    res.set = compileSetter(exp);\n  }\n  expressionCache.put(exp, res);\n  return res;\n}\n\n/**\n * Check if an expression is a simple path.\n *\n * @param {String} exp\n * @return {Boolean}\n */\n\nfunction isSimplePath(exp) {\n  return pathTestRE.test(exp) &&\n  // don't treat literal values as paths\n  !literalValueRE$1.test(exp) &&\n  // Math constants e.g. Math.PI, Math.E etc.\n  exp.slice(0, 5) !== 'Math.';\n}\n\nvar expression = Object.freeze({\n  parseExpression: parseExpression$1,\n  isSimplePath: isSimplePath\n});\n\n// we have two separate queues: one for directive updates\n// and one for user watcher registered via $watch().\n// we want to guarantee directive updates to be called\n// before user watchers so that when user watchers are\n// triggered, the DOM would have already been in updated\n// state.\n\nvar queue = [];\nvar userQueue = [];\nvar has = {};\nvar circular = {};\nvar waiting = false;\n\n/**\n * Reset the batcher's state.\n */\n\nfunction resetBatcherState() {\n  queue.length = 0;\n  userQueue.length = 0;\n  has = {};\n  circular = {};\n  waiting = false;\n}\n\n/**\n * Flush both queues and run the watchers.\n */\n\nfunction flushBatcherQueue() {\n  var _again = true;\n\n  _function: while (_again) {\n    _again = false;\n\n    runBatcherQueue(queue);\n    runBatcherQueue(userQueue);\n    // user watchers triggered more watchers,\n    // keep flushing until it depletes\n    if (queue.length) {\n      _again = true;\n      continue _function;\n    }\n    // dev tool hook\n    /* istanbul ignore if */\n    if (devtools && config.devtools) {\n      devtools.emit('flush');\n    }\n    resetBatcherState();\n  }\n}\n\n/**\n * Run the watchers in a single queue.\n *\n * @param {Array} queue\n */\n\nfunction runBatcherQueue(queue) {\n  // do not cache length because more watchers might be pushed\n  // as we run existing watchers\n  for (var i = 0; i < queue.length; i++) {\n    var watcher = queue[i];\n    var id = watcher.id;\n    has[id] = null;\n    watcher.run();\n    // in dev build, check and stop circular updates.\n    if ('production' !== 'production' && has[id] != null) {}\n  }\n  queue.length = 0;\n}\n\n/**\n * Push a watcher into the watcher queue.\n * Jobs with duplicate IDs will be skipped unless it's\n * pushed when the queue is being flushed.\n *\n * @param {Watcher} watcher\n *   properties:\n *   - {Number} id\n *   - {Function} run\n */\n\nfunction pushWatcher(watcher) {\n  var id = watcher.id;\n  if (has[id] == null) {\n    // push watcher into appropriate queue\n    var q = watcher.user ? userQueue : queue;\n    has[id] = q.length;\n    q.push(watcher);\n    // queue the flush\n    if (!waiting) {\n      waiting = true;\n      nextTick(flushBatcherQueue);\n    }\n  }\n}\n\nvar uid$2 = 0;\n\n/**\n * A watcher parses an expression, collects dependencies,\n * and fires callback when the expression value changes.\n * This is used for both the $watch() api and directives.\n *\n * @param {Vue} vm\n * @param {String|Function} expOrFn\n * @param {Function} cb\n * @param {Object} options\n *                 - {Array} filters\n *                 - {Boolean} twoWay\n *                 - {Boolean} deep\n *                 - {Boolean} user\n *                 - {Boolean} sync\n *                 - {Boolean} lazy\n *                 - {Function} [preProcess]\n *                 - {Function} [postProcess]\n * @constructor\n */\nfunction Watcher(vm, expOrFn, cb, options) {\n  // mix in options\n  if (options) {\n    extend(this, options);\n  }\n  var isFn = typeof expOrFn === 'function';\n  this.vm = vm;\n  vm._watchers.push(this);\n  this.expression = expOrFn;\n  this.cb = cb;\n  this.id = ++uid$2; // uid for batching\n  this.active = true;\n  this.dirty = this.lazy; // for lazy watchers\n  this.deps = [];\n  this.newDeps = [];\n  this.depIds = new _Set();\n  this.newDepIds = new _Set();\n  this.prevError = null; // for async error stacks\n  // parse expression for getter/setter\n  if (isFn) {\n    this.getter = expOrFn;\n    this.setter = undefined;\n  } else {\n    var res = parseExpression$1(expOrFn, this.twoWay);\n    this.getter = res.get;\n    this.setter = res.set;\n  }\n  this.value = this.lazy ? undefined : this.get();\n  // state for avoiding false triggers for deep and Array\n  // watchers during vm._digest()\n  this.queued = this.shallow = false;\n}\n\n/**\n * Evaluate the getter, and re-collect dependencies.\n */\n\nWatcher.prototype.get = function () {\n  this.beforeGet();\n  var scope = this.scope || this.vm;\n  var value;\n  try {\n    value = this.getter.call(scope, scope);\n  } catch (e) {\n    if ('production' !== 'production' && config.warnExpressionErrors) {}\n  }\n  // \"touch\" every property so they are all tracked as\n  // dependencies for deep watching\n  if (this.deep) {\n    traverse(value);\n  }\n  if (this.preProcess) {\n    value = this.preProcess(value);\n  }\n  if (this.filters) {\n    value = scope._applyFilters(value, null, this.filters, false);\n  }\n  if (this.postProcess) {\n    value = this.postProcess(value);\n  }\n  this.afterGet();\n  return value;\n};\n\n/**\n * Set the corresponding value with the setter.\n *\n * @param {*} value\n */\n\nWatcher.prototype.set = function (value) {\n  var scope = this.scope || this.vm;\n  if (this.filters) {\n    value = scope._applyFilters(value, this.value, this.filters, true);\n  }\n  try {\n    this.setter.call(scope, scope, value);\n  } catch (e) {\n    if ('production' !== 'production' && config.warnExpressionErrors) {}\n  }\n  // two-way sync for v-for alias\n  var forContext = scope.$forContext;\n  if (forContext && forContext.alias === this.expression) {\n    if (forContext.filters) {\n      'production' !== 'production' && warn('It seems you are using two-way binding on ' + 'a v-for alias (' + this.expression + '), and the ' + 'v-for has filters. This will not work properly. ' + 'Either remove the filters or use an array of ' + 'objects and bind to object properties instead.', this.vm);\n      return;\n    }\n    forContext._withLock(function () {\n      if (scope.$key) {\n        // original is an object\n        forContext.rawValue[scope.$key] = value;\n      } else {\n        forContext.rawValue.$set(scope.$index, value);\n      }\n    });\n  }\n};\n\n/**\n * Prepare for dependency collection.\n */\n\nWatcher.prototype.beforeGet = function () {\n  Dep.target = this;\n};\n\n/**\n * Add a dependency to this directive.\n *\n * @param {Dep} dep\n */\n\nWatcher.prototype.addDep = function (dep) {\n  var id = dep.id;\n  if (!this.newDepIds.has(id)) {\n    this.newDepIds.add(id);\n    this.newDeps.push(dep);\n    if (!this.depIds.has(id)) {\n      dep.addSub(this);\n    }\n  }\n};\n\n/**\n * Clean up for dependency collection.\n */\n\nWatcher.prototype.afterGet = function () {\n  Dep.target = null;\n  var i = this.deps.length;\n  while (i--) {\n    var dep = this.deps[i];\n    if (!this.newDepIds.has(dep.id)) {\n      dep.removeSub(this);\n    }\n  }\n  var tmp = this.depIds;\n  this.depIds = this.newDepIds;\n  this.newDepIds = tmp;\n  this.newDepIds.clear();\n  tmp = this.deps;\n  this.deps = this.newDeps;\n  this.newDeps = tmp;\n  this.newDeps.length = 0;\n};\n\n/**\n * Subscriber interface.\n * Will be called when a dependency changes.\n *\n * @param {Boolean} shallow\n */\n\nWatcher.prototype.update = function (shallow) {\n  if (this.lazy) {\n    this.dirty = true;\n  } else if (this.sync || !config.async) {\n    this.run();\n  } else {\n    // if queued, only overwrite shallow with non-shallow,\n    // but not the other way around.\n    this.shallow = this.queued ? shallow ? this.shallow : false : !!shallow;\n    this.queued = true;\n    // record before-push error stack in debug mode\n    /* istanbul ignore if */\n    if ('production' !== 'production' && config.debug) {}\n    pushWatcher(this);\n  }\n};\n\n/**\n * Batcher job interface.\n * Will be called by the batcher.\n */\n\nWatcher.prototype.run = function () {\n  if (this.active) {\n    var value = this.get();\n    if (value !== this.value ||\n    // Deep watchers and watchers on Object/Arrays should fire even\n    // when the value is the same, because the value may\n    // have mutated; but only do so if this is a\n    // non-shallow update (caused by a vm digest).\n    (isObject(value) || this.deep) && !this.shallow) {\n      // set new value\n      var oldValue = this.value;\n      this.value = value;\n      // in debug + async mode, when a watcher callbacks\n      // throws, we also throw the saved before-push error\n      // so the full cross-tick stack trace is available.\n      var prevError = this.prevError;\n      /* istanbul ignore if */\n      if ('production' !== 'production' && config.debug && prevError) {} else {\n        this.cb.call(this.vm, value, oldValue);\n      }\n    }\n    this.queued = this.shallow = false;\n  }\n};\n\n/**\n * Evaluate the value of the watcher.\n * This only gets called for lazy watchers.\n */\n\nWatcher.prototype.evaluate = function () {\n  // avoid overwriting another watcher that is being\n  // collected.\n  var current = Dep.target;\n  this.value = this.get();\n  this.dirty = false;\n  Dep.target = current;\n};\n\n/**\n * Depend on all deps collected by this watcher.\n */\n\nWatcher.prototype.depend = function () {\n  var i = this.deps.length;\n  while (i--) {\n    this.deps[i].depend();\n  }\n};\n\n/**\n * Remove self from all dependencies' subcriber list.\n */\n\nWatcher.prototype.teardown = function () {\n  if (this.active) {\n    // remove self from vm's watcher list\n    // this is a somewhat expensive operation so we skip it\n    // if the vm is being destroyed or is performing a v-for\n    // re-render (the watcher list is then filtered by v-for).\n    if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) {\n      this.vm._watchers.$remove(this);\n    }\n    var i = this.deps.length;\n    while (i--) {\n      this.deps[i].removeSub(this);\n    }\n    this.active = false;\n    this.vm = this.cb = this.value = null;\n  }\n};\n\n/**\n * Recrusively traverse an object to evoke all converted\n * getters, so that every nested property inside the object\n * is collected as a \"deep\" dependency.\n *\n * @param {*} val\n */\n\nvar seenObjects = new _Set();\nfunction traverse(val, seen) {\n  var i = undefined,\n      keys = undefined;\n  if (!seen) {\n    seen = seenObjects;\n    seen.clear();\n  }\n  var isA = isArray(val);\n  var isO = isObject(val);\n  if ((isA || isO) && Object.isExtensible(val)) {\n    if (val.__ob__) {\n      var depId = val.__ob__.dep.id;\n      if (seen.has(depId)) {\n        return;\n      } else {\n        seen.add(depId);\n      }\n    }\n    if (isA) {\n      i = val.length;\n      while (i--) traverse(val[i], seen);\n    } else if (isO) {\n      keys = Object.keys(val);\n      i = keys.length;\n      while (i--) traverse(val[keys[i]], seen);\n    }\n  }\n}\n\nvar text$1 = {\n\n  bind: function bind() {\n    this.attr = this.el.nodeType === 3 ? 'data' : 'textContent';\n  },\n\n  update: function update(value) {\n    this.el[this.attr] = _toString(value);\n  }\n};\n\nvar templateCache = new Cache(1000);\nvar idSelectorCache = new Cache(1000);\n\nvar map = {\n  efault: [0, '', ''],\n  legend: [1, '<fieldset>', '</fieldset>'],\n  tr: [2, '<table><tbody>', '</tbody></table>'],\n  col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>']\n};\n\nmap.td = map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'];\n\nmap.option = map.optgroup = [1, '<select multiple=\"multiple\">', '</select>'];\n\nmap.thead = map.tbody = map.colgroup = map.caption = map.tfoot = [1, '<table>', '</table>'];\n\nmap.g = map.defs = map.symbol = map.use = map.image = map.text = map.circle = map.ellipse = map.line = map.path = map.polygon = map.polyline = map.rect = [1, '<svg ' + 'xmlns=\"http://www.w3.org/2000/svg\" ' + 'xmlns:xlink=\"http://www.w3.org/1999/xlink\" ' + 'xmlns:ev=\"http://www.w3.org/2001/xml-events\"' + 'version=\"1.1\">', '</svg>'];\n\n/**\n * Check if a node is a supported template node with a\n * DocumentFragment content.\n *\n * @param {Node} node\n * @return {Boolean}\n */\n\nfunction isRealTemplate(node) {\n  return isTemplate(node) && isFragment(node.content);\n}\n\nvar tagRE$1 = /<([\\w:-]+)/;\nvar entityRE = /&#?\\w+?;/;\nvar commentRE = /<!--/;\n\n/**\n * Convert a string template to a DocumentFragment.\n * Determines correct wrapping by tag types. Wrapping\n * strategy found in jQuery & component/domify.\n *\n * @param {String} templateString\n * @param {Boolean} raw\n * @return {DocumentFragment}\n */\n\nfunction stringToFragment(templateString, raw) {\n  // try a cache hit first\n  var cacheKey = raw ? templateString : templateString.trim();\n  var hit = templateCache.get(cacheKey);\n  if (hit) {\n    return hit;\n  }\n\n  var frag = document.createDocumentFragment();\n  var tagMatch = templateString.match(tagRE$1);\n  var entityMatch = entityRE.test(templateString);\n  var commentMatch = commentRE.test(templateString);\n\n  if (!tagMatch && !entityMatch && !commentMatch) {\n    // text only, return a single text node.\n    frag.appendChild(document.createTextNode(templateString));\n  } else {\n    var tag = tagMatch && tagMatch[1];\n    var wrap = map[tag] || map.efault;\n    var depth = wrap[0];\n    var prefix = wrap[1];\n    var suffix = wrap[2];\n    var node = document.createElement('div');\n\n    node.innerHTML = prefix + templateString + suffix;\n    while (depth--) {\n      node = node.lastChild;\n    }\n\n    var child;\n    /* eslint-disable no-cond-assign */\n    while (child = node.firstChild) {\n      /* eslint-enable no-cond-assign */\n      frag.appendChild(child);\n    }\n  }\n  if (!raw) {\n    trimNode(frag);\n  }\n  templateCache.put(cacheKey, frag);\n  return frag;\n}\n\n/**\n * Convert a template node to a DocumentFragment.\n *\n * @param {Node} node\n * @return {DocumentFragment}\n */\n\nfunction nodeToFragment(node) {\n  // if its a template tag and the browser supports it,\n  // its content is already a document fragment. However, iOS Safari has\n  // bug when using directly cloned template content with touch\n  // events and can cause crashes when the nodes are removed from DOM, so we\n  // have to treat template elements as string templates. (#2805)\n  /* istanbul ignore if */\n  if (isRealTemplate(node)) {\n    return stringToFragment(node.innerHTML);\n  }\n  // script template\n  if (node.tagName === 'SCRIPT') {\n    return stringToFragment(node.textContent);\n  }\n  // normal node, clone it to avoid mutating the original\n  var clonedNode = cloneNode(node);\n  var frag = document.createDocumentFragment();\n  var child;\n  /* eslint-disable no-cond-assign */\n  while (child = clonedNode.firstChild) {\n    /* eslint-enable no-cond-assign */\n    frag.appendChild(child);\n  }\n  trimNode(frag);\n  return frag;\n}\n\n// Test for the presence of the Safari template cloning bug\n// https://bugs.webkit.org/showug.cgi?id=137755\nvar hasBrokenTemplate = (function () {\n  /* istanbul ignore else */\n  if (inBrowser) {\n    var a = document.createElement('div');\n    a.innerHTML = '<template>1</template>';\n    return !a.cloneNode(true).firstChild.innerHTML;\n  } else {\n    return false;\n  }\n})();\n\n// Test for IE10/11 textarea placeholder clone bug\nvar hasTextareaCloneBug = (function () {\n  /* istanbul ignore else */\n  if (inBrowser) {\n    var t = document.createElement('textarea');\n    t.placeholder = 't';\n    return t.cloneNode(true).value === 't';\n  } else {\n    return false;\n  }\n})();\n\n/**\n * 1. Deal with Safari cloning nested <template> bug by\n *    manually cloning all template instances.\n * 2. Deal with IE10/11 textarea placeholder bug by setting\n *    the correct value after cloning.\n *\n * @param {Element|DocumentFragment} node\n * @return {Element|DocumentFragment}\n */\n\nfunction cloneNode(node) {\n  /* istanbul ignore if */\n  if (!node.querySelectorAll) {\n    return node.cloneNode();\n  }\n  var res = node.cloneNode(true);\n  var i, original, cloned;\n  /* istanbul ignore if */\n  if (hasBrokenTemplate) {\n    var tempClone = res;\n    if (isRealTemplate(node)) {\n      node = node.content;\n      tempClone = res.content;\n    }\n    original = node.querySelectorAll('template');\n    if (original.length) {\n      cloned = tempClone.querySelectorAll('template');\n      i = cloned.length;\n      while (i--) {\n        cloned[i].parentNode.replaceChild(cloneNode(original[i]), cloned[i]);\n      }\n    }\n  }\n  /* istanbul ignore if */\n  if (hasTextareaCloneBug) {\n    if (node.tagName === 'TEXTAREA') {\n      res.value = node.value;\n    } else {\n      original = node.querySelectorAll('textarea');\n      if (original.length) {\n        cloned = res.querySelectorAll('textarea');\n        i = cloned.length;\n        while (i--) {\n          cloned[i].value = original[i].value;\n        }\n      }\n    }\n  }\n  return res;\n}\n\n/**\n * Process the template option and normalizes it into a\n * a DocumentFragment that can be used as a partial or a\n * instance template.\n *\n * @param {*} template\n *        Possible values include:\n *        - DocumentFragment object\n *        - Node object of type Template\n *        - id selector: '#some-template-id'\n *        - template string: '<div><span>{{msg}}</span></div>'\n * @param {Boolean} shouldClone\n * @param {Boolean} raw\n *        inline HTML interpolation. Do not check for id\n *        selector and keep whitespace in the string.\n * @return {DocumentFragment|undefined}\n */\n\nfunction parseTemplate(template, shouldClone, raw) {\n  var node, frag;\n\n  // if the template is already a document fragment,\n  // do nothing\n  if (isFragment(template)) {\n    trimNode(template);\n    return shouldClone ? cloneNode(template) : template;\n  }\n\n  if (typeof template === 'string') {\n    // id selector\n    if (!raw && template.charAt(0) === '#') {\n      // id selector can be cached too\n      frag = idSelectorCache.get(template);\n      if (!frag) {\n        node = document.getElementById(template.slice(1));\n        if (node) {\n          frag = nodeToFragment(node);\n          // save selector to cache\n          idSelectorCache.put(template, frag);\n        }\n      }\n    } else {\n      // normal string template\n      frag = stringToFragment(template, raw);\n    }\n  } else if (template.nodeType) {\n    // a direct node\n    frag = nodeToFragment(template);\n  }\n\n  return frag && shouldClone ? cloneNode(frag) : frag;\n}\n\nvar template = Object.freeze({\n  cloneNode: cloneNode,\n  parseTemplate: parseTemplate\n});\n\nvar html = {\n\n  bind: function bind() {\n    // a comment node means this is a binding for\n    // {{{ inline unescaped html }}}\n    if (this.el.nodeType === 8) {\n      // hold nodes\n      this.nodes = [];\n      // replace the placeholder with proper anchor\n      this.anchor = createAnchor('v-html');\n      replace(this.el, this.anchor);\n    }\n  },\n\n  update: function update(value) {\n    value = _toString(value);\n    if (this.nodes) {\n      this.swap(value);\n    } else {\n      this.el.innerHTML = value;\n    }\n  },\n\n  swap: function swap(value) {\n    // remove old nodes\n    var i = this.nodes.length;\n    while (i--) {\n      remove(this.nodes[i]);\n    }\n    // convert new value to a fragment\n    // do not attempt to retrieve from id selector\n    var frag = parseTemplate(value, true, true);\n    // save a reference to these nodes so we can remove later\n    this.nodes = toArray(frag.childNodes);\n    before(frag, this.anchor);\n  }\n};\n\n/**\n * Abstraction for a partially-compiled fragment.\n * Can optionally compile content with a child scope.\n *\n * @param {Function} linker\n * @param {Vue} vm\n * @param {DocumentFragment} frag\n * @param {Vue} [host]\n * @param {Object} [scope]\n * @param {Fragment} [parentFrag]\n */\nfunction Fragment(linker, vm, frag, host, scope, parentFrag) {\n  this.children = [];\n  this.childFrags = [];\n  this.vm = vm;\n  this.scope = scope;\n  this.inserted = false;\n  this.parentFrag = parentFrag;\n  if (parentFrag) {\n    parentFrag.childFrags.push(this);\n  }\n  this.unlink = linker(vm, frag, host, scope, this);\n  var single = this.single = frag.childNodes.length === 1 &&\n  // do not go single mode if the only node is an anchor\n  !frag.childNodes[0].__v_anchor;\n  if (single) {\n    this.node = frag.childNodes[0];\n    this.before = singleBefore;\n    this.remove = singleRemove;\n  } else {\n    this.node = createAnchor('fragment-start');\n    this.end = createAnchor('fragment-end');\n    this.frag = frag;\n    prepend(this.node, frag);\n    frag.appendChild(this.end);\n    this.before = multiBefore;\n    this.remove = multiRemove;\n  }\n  this.node.__v_frag = this;\n}\n\n/**\n * Call attach/detach for all components contained within\n * this fragment. Also do so recursively for all child\n * fragments.\n *\n * @param {Function} hook\n */\n\nFragment.prototype.callHook = function (hook) {\n  var i, l;\n  for (i = 0, l = this.childFrags.length; i < l; i++) {\n    this.childFrags[i].callHook(hook);\n  }\n  for (i = 0, l = this.children.length; i < l; i++) {\n    hook(this.children[i]);\n  }\n};\n\n/**\n * Insert fragment before target, single node version\n *\n * @param {Node} target\n * @param {Boolean} withTransition\n */\n\nfunction singleBefore(target, withTransition) {\n  this.inserted = true;\n  var method = withTransition !== false ? beforeWithTransition : before;\n  method(this.node, target, this.vm);\n  if (inDoc(this.node)) {\n    this.callHook(attach);\n  }\n}\n\n/**\n * Remove fragment, single node version\n */\n\nfunction singleRemove() {\n  this.inserted = false;\n  var shouldCallRemove = inDoc(this.node);\n  var self = this;\n  this.beforeRemove();\n  removeWithTransition(this.node, this.vm, function () {\n    if (shouldCallRemove) {\n      self.callHook(detach);\n    }\n    self.destroy();\n  });\n}\n\n/**\n * Insert fragment before target, multi-nodes version\n *\n * @param {Node} target\n * @param {Boolean} withTransition\n */\n\nfunction multiBefore(target, withTransition) {\n  this.inserted = true;\n  var vm = this.vm;\n  var method = withTransition !== false ? beforeWithTransition : before;\n  mapNodeRange(this.node, this.end, function (node) {\n    method(node, target, vm);\n  });\n  if (inDoc(this.node)) {\n    this.callHook(attach);\n  }\n}\n\n/**\n * Remove fragment, multi-nodes version\n */\n\nfunction multiRemove() {\n  this.inserted = false;\n  var self = this;\n  var shouldCallRemove = inDoc(this.node);\n  this.beforeRemove();\n  removeNodeRange(this.node, this.end, this.vm, this.frag, function () {\n    if (shouldCallRemove) {\n      self.callHook(detach);\n    }\n    self.destroy();\n  });\n}\n\n/**\n * Prepare the fragment for removal.\n */\n\nFragment.prototype.beforeRemove = function () {\n  var i, l;\n  for (i = 0, l = this.childFrags.length; i < l; i++) {\n    // call the same method recursively on child\n    // fragments, depth-first\n    this.childFrags[i].beforeRemove(false);\n  }\n  for (i = 0, l = this.children.length; i < l; i++) {\n    // Call destroy for all contained instances,\n    // with remove:false and defer:true.\n    // Defer is necessary because we need to\n    // keep the children to call detach hooks\n    // on them.\n    this.children[i].$destroy(false, true);\n  }\n  var dirs = this.unlink.dirs;\n  for (i = 0, l = dirs.length; i < l; i++) {\n    // disable the watchers on all the directives\n    // so that the rendered content stays the same\n    // during removal.\n    dirs[i]._watcher && dirs[i]._watcher.teardown();\n  }\n};\n\n/**\n * Destroy the fragment.\n */\n\nFragment.prototype.destroy = function () {\n  if (this.parentFrag) {\n    this.parentFrag.childFrags.$remove(this);\n  }\n  this.node.__v_frag = null;\n  this.unlink();\n};\n\n/**\n * Call attach hook for a Vue instance.\n *\n * @param {Vue} child\n */\n\nfunction attach(child) {\n  if (!child._isAttached && inDoc(child.$el)) {\n    child._callHook('attached');\n  }\n}\n\n/**\n * Call detach hook for a Vue instance.\n *\n * @param {Vue} child\n */\n\nfunction detach(child) {\n  if (child._isAttached && !inDoc(child.$el)) {\n    child._callHook('detached');\n  }\n}\n\nvar linkerCache = new Cache(5000);\n\n/**\n * A factory that can be used to create instances of a\n * fragment. Caches the compiled linker if possible.\n *\n * @param {Vue} vm\n * @param {Element|String} el\n */\nfunction FragmentFactory(vm, el) {\n  this.vm = vm;\n  var template;\n  var isString = typeof el === 'string';\n  if (isString || isTemplate(el) && !el.hasAttribute('v-if')) {\n    template = parseTemplate(el, true);\n  } else {\n    template = document.createDocumentFragment();\n    template.appendChild(el);\n  }\n  this.template = template;\n  // linker can be cached, but only for components\n  var linker;\n  var cid = vm.constructor.cid;\n  if (cid > 0) {\n    var cacheId = cid + (isString ? el : getOuterHTML(el));\n    linker = linkerCache.get(cacheId);\n    if (!linker) {\n      linker = compile(template, vm.$options, true);\n      linkerCache.put(cacheId, linker);\n    }\n  } else {\n    linker = compile(template, vm.$options, true);\n  }\n  this.linker = linker;\n}\n\n/**\n * Create a fragment instance with given host and scope.\n *\n * @param {Vue} host\n * @param {Object} scope\n * @param {Fragment} parentFrag\n */\n\nFragmentFactory.prototype.create = function (host, scope, parentFrag) {\n  var frag = cloneNode(this.template);\n  return new Fragment(this.linker, this.vm, frag, host, scope, parentFrag);\n};\n\nvar ON = 700;\nvar MODEL = 800;\nvar BIND = 850;\nvar TRANSITION = 1100;\nvar EL = 1500;\nvar COMPONENT = 1500;\nvar PARTIAL = 1750;\nvar IF = 2100;\nvar FOR = 2200;\nvar SLOT = 2300;\n\nvar uid$3 = 0;\n\nvar vFor = {\n\n  priority: FOR,\n  terminal: true,\n\n  params: ['track-by', 'stagger', 'enter-stagger', 'leave-stagger'],\n\n  bind: function bind() {\n    if ('production' !== 'production' && this.el.hasAttribute('v-if')) {}\n\n    // support \"item in/of items\" syntax\n    var inMatch = this.expression.match(/(.*) (?:in|of) (.*)/);\n    if (inMatch) {\n      var itMatch = inMatch[1].match(/\\((.*),(.*)\\)/);\n      if (itMatch) {\n        this.iterator = itMatch[1].trim();\n        this.alias = itMatch[2].trim();\n      } else {\n        this.alias = inMatch[1].trim();\n      }\n      this.expression = inMatch[2];\n    }\n\n    if (!this.alias) {\n      'production' !== 'production' && warn('Invalid v-for expression \"' + this.descriptor.raw + '\": ' + 'alias is required.', this.vm);\n      return;\n    }\n\n    // uid as a cache identifier\n    this.id = '__v-for__' + ++uid$3;\n\n    // check if this is an option list,\n    // so that we know if we need to update the <select>'s\n    // v-model when the option list has changed.\n    // because v-model has a lower priority than v-for,\n    // the v-model is not bound here yet, so we have to\n    // retrive it in the actual updateModel() function.\n    var tag = this.el.tagName;\n    this.isOption = (tag === 'OPTION' || tag === 'OPTGROUP') && this.el.parentNode.tagName === 'SELECT';\n\n    // setup anchor nodes\n    this.start = createAnchor('v-for-start');\n    this.end = createAnchor('v-for-end');\n    replace(this.el, this.end);\n    before(this.start, this.end);\n\n    // cache\n    this.cache = Object.create(null);\n\n    // fragment factory\n    this.factory = new FragmentFactory(this.vm, this.el);\n  },\n\n  update: function update(data) {\n    this.diff(data);\n    this.updateRef();\n    this.updateModel();\n  },\n\n  /**\n   * Diff, based on new data and old data, determine the\n   * minimum amount of DOM manipulations needed to make the\n   * DOM reflect the new data Array.\n   *\n   * The algorithm diffs the new data Array by storing a\n   * hidden reference to an owner vm instance on previously\n   * seen data. This allows us to achieve O(n) which is\n   * better than a levenshtein distance based algorithm,\n   * which is O(m * n).\n   *\n   * @param {Array} data\n   */\n\n  diff: function diff(data) {\n    // check if the Array was converted from an Object\n    var item = data[0];\n    var convertedFromObject = this.fromObject = isObject(item) && hasOwn(item, '$key') && hasOwn(item, '$value');\n\n    var trackByKey = this.params.trackBy;\n    var oldFrags = this.frags;\n    var frags = this.frags = new Array(data.length);\n    var alias = this.alias;\n    var iterator = this.iterator;\n    var start = this.start;\n    var end = this.end;\n    var inDocument = inDoc(start);\n    var init = !oldFrags;\n    var i, l, frag, key, value, primitive;\n\n    // First pass, go through the new Array and fill up\n    // the new frags array. If a piece of data has a cached\n    // instance for it, we reuse it. Otherwise build a new\n    // instance.\n    for (i = 0, l = data.length; i < l; i++) {\n      item = data[i];\n      key = convertedFromObject ? item.$key : null;\n      value = convertedFromObject ? item.$value : item;\n      primitive = !isObject(value);\n      frag = !init && this.getCachedFrag(value, i, key);\n      if (frag) {\n        // reusable fragment\n        frag.reused = true;\n        // update $index\n        frag.scope.$index = i;\n        // update $key\n        if (key) {\n          frag.scope.$key = key;\n        }\n        // update iterator\n        if (iterator) {\n          frag.scope[iterator] = key !== null ? key : i;\n        }\n        // update data for track-by, object repeat &\n        // primitive values.\n        if (trackByKey || convertedFromObject || primitive) {\n          withoutConversion(function () {\n            frag.scope[alias] = value;\n          });\n        }\n      } else {\n        // new instance\n        frag = this.create(value, alias, i, key);\n        frag.fresh = !init;\n      }\n      frags[i] = frag;\n      if (init) {\n        frag.before(end);\n      }\n    }\n\n    // we're done for the initial render.\n    if (init) {\n      return;\n    }\n\n    // Second pass, go through the old fragments and\n    // destroy those who are not reused (and remove them\n    // from cache)\n    var removalIndex = 0;\n    var totalRemoved = oldFrags.length - frags.length;\n    // when removing a large number of fragments, watcher removal\n    // turns out to be a perf bottleneck, so we batch the watcher\n    // removals into a single filter call!\n    this.vm._vForRemoving = true;\n    for (i = 0, l = oldFrags.length; i < l; i++) {\n      frag = oldFrags[i];\n      if (!frag.reused) {\n        this.deleteCachedFrag(frag);\n        this.remove(frag, removalIndex++, totalRemoved, inDocument);\n      }\n    }\n    this.vm._vForRemoving = false;\n    if (removalIndex) {\n      this.vm._watchers = this.vm._watchers.filter(function (w) {\n        return w.active;\n      });\n    }\n\n    // Final pass, move/insert new fragments into the\n    // right place.\n    var targetPrev, prevEl, currentPrev;\n    var insertionIndex = 0;\n    for (i = 0, l = frags.length; i < l; i++) {\n      frag = frags[i];\n      // this is the frag that we should be after\n      targetPrev = frags[i - 1];\n      prevEl = targetPrev ? targetPrev.staggerCb ? targetPrev.staggerAnchor : targetPrev.end || targetPrev.node : start;\n      if (frag.reused && !frag.staggerCb) {\n        currentPrev = findPrevFrag(frag, start, this.id);\n        if (currentPrev !== targetPrev && (!currentPrev ||\n        // optimization for moving a single item.\n        // thanks to suggestions by @livoras in #1807\n        findPrevFrag(currentPrev, start, this.id) !== targetPrev)) {\n          this.move(frag, prevEl);\n        }\n      } else {\n        // new instance, or still in stagger.\n        // insert with updated stagger index.\n        this.insert(frag, insertionIndex++, prevEl, inDocument);\n      }\n      frag.reused = frag.fresh = false;\n    }\n  },\n\n  /**\n   * Create a new fragment instance.\n   *\n   * @param {*} value\n   * @param {String} alias\n   * @param {Number} index\n   * @param {String} [key]\n   * @return {Fragment}\n   */\n\n  create: function create(value, alias, index, key) {\n    var host = this._host;\n    // create iteration scope\n    var parentScope = this._scope || this.vm;\n    var scope = Object.create(parentScope);\n    // ref holder for the scope\n    scope.$refs = Object.create(parentScope.$refs);\n    scope.$els = Object.create(parentScope.$els);\n    // make sure point $parent to parent scope\n    scope.$parent = parentScope;\n    // for two-way binding on alias\n    scope.$forContext = this;\n    // define scope properties\n    // important: define the scope alias without forced conversion\n    // so that frozen data structures remain non-reactive.\n    withoutConversion(function () {\n      defineReactive(scope, alias, value);\n    });\n    defineReactive(scope, '$index', index);\n    if (key) {\n      defineReactive(scope, '$key', key);\n    } else if (scope.$key) {\n      // avoid accidental fallback\n      def(scope, '$key', null);\n    }\n    if (this.iterator) {\n      defineReactive(scope, this.iterator, key !== null ? key : index);\n    }\n    var frag = this.factory.create(host, scope, this._frag);\n    frag.forId = this.id;\n    this.cacheFrag(value, frag, index, key);\n    return frag;\n  },\n\n  /**\n   * Update the v-ref on owner vm.\n   */\n\n  updateRef: function updateRef() {\n    var ref = this.descriptor.ref;\n    if (!ref) return;\n    var hash = (this._scope || this.vm).$refs;\n    var refs;\n    if (!this.fromObject) {\n      refs = this.frags.map(findVmFromFrag);\n    } else {\n      refs = {};\n      this.frags.forEach(function (frag) {\n        refs[frag.scope.$key] = findVmFromFrag(frag);\n      });\n    }\n    hash[ref] = refs;\n  },\n\n  /**\n   * For option lists, update the containing v-model on\n   * parent <select>.\n   */\n\n  updateModel: function updateModel() {\n    if (this.isOption) {\n      var parent = this.start.parentNode;\n      var model = parent && parent.__v_model;\n      if (model) {\n        model.forceUpdate();\n      }\n    }\n  },\n\n  /**\n   * Insert a fragment. Handles staggering.\n   *\n   * @param {Fragment} frag\n   * @param {Number} index\n   * @param {Node} prevEl\n   * @param {Boolean} inDocument\n   */\n\n  insert: function insert(frag, index, prevEl, inDocument) {\n    if (frag.staggerCb) {\n      frag.staggerCb.cancel();\n      frag.staggerCb = null;\n    }\n    var staggerAmount = this.getStagger(frag, index, null, 'enter');\n    if (inDocument && staggerAmount) {\n      // create an anchor and insert it synchronously,\n      // so that we can resolve the correct order without\n      // worrying about some elements not inserted yet\n      var anchor = frag.staggerAnchor;\n      if (!anchor) {\n        anchor = frag.staggerAnchor = createAnchor('stagger-anchor');\n        anchor.__v_frag = frag;\n      }\n      after(anchor, prevEl);\n      var op = frag.staggerCb = cancellable(function () {\n        frag.staggerCb = null;\n        frag.before(anchor);\n        remove(anchor);\n      });\n      setTimeout(op, staggerAmount);\n    } else {\n      var target = prevEl.nextSibling;\n      /* istanbul ignore if */\n      if (!target) {\n        // reset end anchor position in case the position was messed up\n        // by an external drag-n-drop library.\n        after(this.end, prevEl);\n        target = this.end;\n      }\n      frag.before(target);\n    }\n  },\n\n  /**\n   * Remove a fragment. Handles staggering.\n   *\n   * @param {Fragment} frag\n   * @param {Number} index\n   * @param {Number} total\n   * @param {Boolean} inDocument\n   */\n\n  remove: function remove(frag, index, total, inDocument) {\n    if (frag.staggerCb) {\n      frag.staggerCb.cancel();\n      frag.staggerCb = null;\n      // it's not possible for the same frag to be removed\n      // twice, so if we have a pending stagger callback,\n      // it means this frag is queued for enter but removed\n      // before its transition started. Since it is already\n      // destroyed, we can just leave it in detached state.\n      return;\n    }\n    var staggerAmount = this.getStagger(frag, index, total, 'leave');\n    if (inDocument && staggerAmount) {\n      var op = frag.staggerCb = cancellable(function () {\n        frag.staggerCb = null;\n        frag.remove();\n      });\n      setTimeout(op, staggerAmount);\n    } else {\n      frag.remove();\n    }\n  },\n\n  /**\n   * Move a fragment to a new position.\n   * Force no transition.\n   *\n   * @param {Fragment} frag\n   * @param {Node} prevEl\n   */\n\n  move: function move(frag, prevEl) {\n    // fix a common issue with Sortable:\n    // if prevEl doesn't have nextSibling, this means it's\n    // been dragged after the end anchor. Just re-position\n    // the end anchor to the end of the container.\n    /* istanbul ignore if */\n    if (!prevEl.nextSibling) {\n      this.end.parentNode.appendChild(this.end);\n    }\n    frag.before(prevEl.nextSibling, false);\n  },\n\n  /**\n   * Cache a fragment using track-by or the object key.\n   *\n   * @param {*} value\n   * @param {Fragment} frag\n   * @param {Number} index\n   * @param {String} [key]\n   */\n\n  cacheFrag: function cacheFrag(value, frag, index, key) {\n    var trackByKey = this.params.trackBy;\n    var cache = this.cache;\n    var primitive = !isObject(value);\n    var id;\n    if (key || trackByKey || primitive) {\n      id = getTrackByKey(index, key, value, trackByKey);\n      if (!cache[id]) {\n        cache[id] = frag;\n      } else if (trackByKey !== '$index') {\n        'production' !== 'production' && this.warnDuplicate(value);\n      }\n    } else {\n      id = this.id;\n      if (hasOwn(value, id)) {\n        if (value[id] === null) {\n          value[id] = frag;\n        } else {\n          'production' !== 'production' && this.warnDuplicate(value);\n        }\n      } else if (Object.isExtensible(value)) {\n        def(value, id, frag);\n      } else if ('production' !== 'production') {}\n    }\n    frag.raw = value;\n  },\n\n  /**\n   * Get a cached fragment from the value/index/key\n   *\n   * @param {*} value\n   * @param {Number} index\n   * @param {String} key\n   * @return {Fragment}\n   */\n\n  getCachedFrag: function getCachedFrag(value, index, key) {\n    var trackByKey = this.params.trackBy;\n    var primitive = !isObject(value);\n    var frag;\n    if (key || trackByKey || primitive) {\n      var id = getTrackByKey(index, key, value, trackByKey);\n      frag = this.cache[id];\n    } else {\n      frag = value[this.id];\n    }\n    if (frag && (frag.reused || frag.fresh)) {\n      'production' !== 'production' && this.warnDuplicate(value);\n    }\n    return frag;\n  },\n\n  /**\n   * Delete a fragment from cache.\n   *\n   * @param {Fragment} frag\n   */\n\n  deleteCachedFrag: function deleteCachedFrag(frag) {\n    var value = frag.raw;\n    var trackByKey = this.params.trackBy;\n    var scope = frag.scope;\n    var index = scope.$index;\n    // fix #948: avoid accidentally fall through to\n    // a parent repeater which happens to have $key.\n    var key = hasOwn(scope, '$key') && scope.$key;\n    var primitive = !isObject(value);\n    if (trackByKey || key || primitive) {\n      var id = getTrackByKey(index, key, value, trackByKey);\n      this.cache[id] = null;\n    } else {\n      value[this.id] = null;\n      frag.raw = null;\n    }\n  },\n\n  /**\n   * Get the stagger amount for an insertion/removal.\n   *\n   * @param {Fragment} frag\n   * @param {Number} index\n   * @param {Number} total\n   * @param {String} type\n   */\n\n  getStagger: function getStagger(frag, index, total, type) {\n    type = type + 'Stagger';\n    var trans = frag.node.__v_trans;\n    var hooks = trans && trans.hooks;\n    var hook = hooks && (hooks[type] || hooks.stagger);\n    return hook ? hook.call(frag, index, total) : index * parseInt(this.params[type] || this.params.stagger, 10);\n  },\n\n  /**\n   * Pre-process the value before piping it through the\n   * filters. This is passed to and called by the watcher.\n   */\n\n  _preProcess: function _preProcess(value) {\n    // regardless of type, store the un-filtered raw value.\n    this.rawValue = value;\n    return value;\n  },\n\n  /**\n   * Post-process the value after it has been piped through\n   * the filters. This is passed to and called by the watcher.\n   *\n   * It is necessary for this to be called during the\n   * watcher's dependency collection phase because we want\n   * the v-for to update when the source Object is mutated.\n   */\n\n  _postProcess: function _postProcess(value) {\n    if (isArray(value)) {\n      return value;\n    } else if (isPlainObject(value)) {\n      // convert plain object to array.\n      var keys = Object.keys(value);\n      var i = keys.length;\n      var res = new Array(i);\n      var key;\n      while (i--) {\n        key = keys[i];\n        res[i] = {\n          $key: key,\n          $value: value[key]\n        };\n      }\n      return res;\n    } else {\n      if (typeof value === 'number' && !isNaN(value)) {\n        value = range(value);\n      }\n      return value || [];\n    }\n  },\n\n  unbind: function unbind() {\n    if (this.descriptor.ref) {\n      (this._scope || this.vm).$refs[this.descriptor.ref] = null;\n    }\n    if (this.frags) {\n      var i = this.frags.length;\n      var frag;\n      while (i--) {\n        frag = this.frags[i];\n        this.deleteCachedFrag(frag);\n        frag.destroy();\n      }\n    }\n  }\n};\n\n/**\n * Helper to find the previous element that is a fragment\n * anchor. This is necessary because a destroyed frag's\n * element could still be lingering in the DOM before its\n * leaving transition finishes, but its inserted flag\n * should have been set to false so we can skip them.\n *\n * If this is a block repeat, we want to make sure we only\n * return frag that is bound to this v-for. (see #929)\n *\n * @param {Fragment} frag\n * @param {Comment|Text} anchor\n * @param {String} id\n * @return {Fragment}\n */\n\nfunction findPrevFrag(frag, anchor, id) {\n  var el = frag.node.previousSibling;\n  /* istanbul ignore if */\n  if (!el) return;\n  frag = el.__v_frag;\n  while ((!frag || frag.forId !== id || !frag.inserted) && el !== anchor) {\n    el = el.previousSibling;\n    /* istanbul ignore if */\n    if (!el) return;\n    frag = el.__v_frag;\n  }\n  return frag;\n}\n\n/**\n * Create a range array from given number.\n *\n * @param {Number} n\n * @return {Array}\n */\n\nfunction range(n) {\n  var i = -1;\n  var ret = new Array(Math.floor(n));\n  while (++i < n) {\n    ret[i] = i;\n  }\n  return ret;\n}\n\n/**\n * Get the track by key for an item.\n *\n * @param {Number} index\n * @param {String} key\n * @param {*} value\n * @param {String} [trackByKey]\n */\n\nfunction getTrackByKey(index, key, value, trackByKey) {\n  return trackByKey ? trackByKey === '$index' ? index : trackByKey.charAt(0).match(/\\w/) ? getPath(value, trackByKey) : value[trackByKey] : key || value;\n}\n\n/**\n * Find a vm from a fragment.\n *\n * @param {Fragment} frag\n * @return {Vue|undefined}\n */\n\nfunction findVmFromFrag(frag) {\n  var node = frag.node;\n  // handle multi-node frag\n  if (frag.end) {\n    while (!node.__vue__ && node !== frag.end && node.nextSibling) {\n      node = node.nextSibling;\n    }\n  }\n  return node.__vue__;\n}\n\nvar vIf = {\n\n  priority: IF,\n  terminal: true,\n\n  bind: function bind() {\n    var el = this.el;\n    if (!el.__vue__) {\n      // check else block\n      var next = el.nextElementSibling;\n      if (next && getAttr(next, 'v-else') !== null) {\n        remove(next);\n        this.elseEl = next;\n      }\n      // check main block\n      this.anchor = createAnchor('v-if');\n      replace(el, this.anchor);\n    } else {\n      'production' !== 'production' && warn('v-if=\"' + this.expression + '\" cannot be ' + 'used on an instance root element.', this.vm);\n      this.invalid = true;\n    }\n  },\n\n  update: function update(value) {\n    if (this.invalid) return;\n    if (value) {\n      if (!this.frag) {\n        this.insert();\n      }\n    } else {\n      this.remove();\n    }\n  },\n\n  insert: function insert() {\n    if (this.elseFrag) {\n      this.elseFrag.remove();\n      this.elseFrag = null;\n    }\n    // lazy init factory\n    if (!this.factory) {\n      this.factory = new FragmentFactory(this.vm, this.el);\n    }\n    this.frag = this.factory.create(this._host, this._scope, this._frag);\n    this.frag.before(this.anchor);\n  },\n\n  remove: function remove() {\n    if (this.frag) {\n      this.frag.remove();\n      this.frag = null;\n    }\n    if (this.elseEl && !this.elseFrag) {\n      if (!this.elseFactory) {\n        this.elseFactory = new FragmentFactory(this.elseEl._context || this.vm, this.elseEl);\n      }\n      this.elseFrag = this.elseFactory.create(this._host, this._scope, this._frag);\n      this.elseFrag.before(this.anchor);\n    }\n  },\n\n  unbind: function unbind() {\n    if (this.frag) {\n      this.frag.destroy();\n    }\n    if (this.elseFrag) {\n      this.elseFrag.destroy();\n    }\n  }\n};\n\nvar show = {\n\n  bind: function bind() {\n    // check else block\n    var next = this.el.nextElementSibling;\n    if (next && getAttr(next, 'v-else') !== null) {\n      this.elseEl = next;\n    }\n  },\n\n  update: function update(value) {\n    this.apply(this.el, value);\n    if (this.elseEl) {\n      this.apply(this.elseEl, !value);\n    }\n  },\n\n  apply: function apply(el, value) {\n    if (inDoc(el)) {\n      applyTransition(el, value ? 1 : -1, toggle, this.vm);\n    } else {\n      toggle();\n    }\n    function toggle() {\n      el.style.display = value ? '' : 'none';\n    }\n  }\n};\n\nvar text$2 = {\n\n  bind: function bind() {\n    var self = this;\n    var el = this.el;\n    var isRange = el.type === 'range';\n    var lazy = this.params.lazy;\n    var number = this.params.number;\n    var debounce = this.params.debounce;\n\n    // handle composition events.\n    //   http://blog.evanyou.me/2014/01/03/composition-event/\n    // skip this for Android because it handles composition\n    // events quite differently. Android doesn't trigger\n    // composition events for language input methods e.g.\n    // Chinese, but instead triggers them for spelling\n    // suggestions... (see Discussion/#162)\n    var composing = false;\n    if (!isAndroid && !isRange) {\n      this.on('compositionstart', function () {\n        composing = true;\n      });\n      this.on('compositionend', function () {\n        composing = false;\n        // in IE11 the \"compositionend\" event fires AFTER\n        // the \"input\" event, so the input handler is blocked\n        // at the end... have to call it here.\n        //\n        // #1327: in lazy mode this is unecessary.\n        if (!lazy) {\n          self.listener();\n        }\n      });\n    }\n\n    // prevent messing with the input when user is typing,\n    // and force update on blur.\n    this.focused = false;\n    if (!isRange && !lazy) {\n      this.on('focus', function () {\n        self.focused = true;\n      });\n      this.on('blur', function () {\n        self.focused = false;\n        // do not sync value after fragment removal (#2017)\n        if (!self._frag || self._frag.inserted) {\n          self.rawListener();\n        }\n      });\n    }\n\n    // Now attach the main listener\n    this.listener = this.rawListener = function () {\n      if (composing || !self._bound) {\n        return;\n      }\n      var val = number || isRange ? toNumber(el.value) : el.value;\n      self.set(val);\n      // force update on next tick to avoid lock & same value\n      // also only update when user is not typing\n      nextTick(function () {\n        if (self._bound && !self.focused) {\n          self.update(self._watcher.value);\n        }\n      });\n    };\n\n    // apply debounce\n    if (debounce) {\n      this.listener = _debounce(this.listener, debounce);\n    }\n\n    // Support jQuery events, since jQuery.trigger() doesn't\n    // trigger native events in some cases and some plugins\n    // rely on $.trigger()\n    //\n    // We want to make sure if a listener is attached using\n    // jQuery, it is also removed with jQuery, that's why\n    // we do the check for each directive instance and\n    // store that check result on itself. This also allows\n    // easier test coverage control by unsetting the global\n    // jQuery variable in tests.\n    this.hasjQuery = typeof jQuery === 'function';\n    if (this.hasjQuery) {\n      var method = jQuery.fn.on ? 'on' : 'bind';\n      jQuery(el)[method]('change', this.rawListener);\n      if (!lazy) {\n        jQuery(el)[method]('input', this.listener);\n      }\n    } else {\n      this.on('change', this.rawListener);\n      if (!lazy) {\n        this.on('input', this.listener);\n      }\n    }\n\n    // IE9 doesn't fire input event on backspace/del/cut\n    if (!lazy && isIE9) {\n      this.on('cut', function () {\n        nextTick(self.listener);\n      });\n      this.on('keyup', function (e) {\n        if (e.keyCode === 46 || e.keyCode === 8) {\n          self.listener();\n        }\n      });\n    }\n\n    // set initial value if present\n    if (el.hasAttribute('value') || el.tagName === 'TEXTAREA' && el.value.trim()) {\n      this.afterBind = this.listener;\n    }\n  },\n\n  update: function update(value) {\n    // #3029 only update when the value changes. This prevent\n    // browsers from overwriting values like selectionStart\n    value = _toString(value);\n    if (value !== this.el.value) this.el.value = value;\n  },\n\n  unbind: function unbind() {\n    var el = this.el;\n    if (this.hasjQuery) {\n      var method = jQuery.fn.off ? 'off' : 'unbind';\n      jQuery(el)[method]('change', this.listener);\n      jQuery(el)[method]('input', this.listener);\n    }\n  }\n};\n\nvar radio = {\n\n  bind: function bind() {\n    var self = this;\n    var el = this.el;\n\n    this.getValue = function () {\n      // value overwrite via v-bind:value\n      if (el.hasOwnProperty('_value')) {\n        return el._value;\n      }\n      var val = el.value;\n      if (self.params.number) {\n        val = toNumber(val);\n      }\n      return val;\n    };\n\n    this.listener = function () {\n      self.set(self.getValue());\n    };\n    this.on('change', this.listener);\n\n    if (el.hasAttribute('checked')) {\n      this.afterBind = this.listener;\n    }\n  },\n\n  update: function update(value) {\n    this.el.checked = looseEqual(value, this.getValue());\n  }\n};\n\nvar select = {\n\n  bind: function bind() {\n    var _this = this;\n\n    var self = this;\n    var el = this.el;\n\n    // method to force update DOM using latest value.\n    this.forceUpdate = function () {\n      if (self._watcher) {\n        self.update(self._watcher.get());\n      }\n    };\n\n    // check if this is a multiple select\n    var multiple = this.multiple = el.hasAttribute('multiple');\n\n    // attach listener\n    this.listener = function () {\n      var value = getValue(el, multiple);\n      value = self.params.number ? isArray(value) ? value.map(toNumber) : toNumber(value) : value;\n      self.set(value);\n    };\n    this.on('change', this.listener);\n\n    // if has initial value, set afterBind\n    var initValue = getValue(el, multiple, true);\n    if (multiple && initValue.length || !multiple && initValue !== null) {\n      this.afterBind = this.listener;\n    }\n\n    // All major browsers except Firefox resets\n    // selectedIndex with value -1 to 0 when the element\n    // is appended to a new parent, therefore we have to\n    // force a DOM update whenever that happens...\n    this.vm.$on('hook:attached', function () {\n      nextTick(_this.forceUpdate);\n    });\n    if (!inDoc(el)) {\n      nextTick(this.forceUpdate);\n    }\n  },\n\n  update: function update(value) {\n    var el = this.el;\n    el.selectedIndex = -1;\n    var multi = this.multiple && isArray(value);\n    var options = el.options;\n    var i = options.length;\n    var op, val;\n    while (i--) {\n      op = options[i];\n      val = op.hasOwnProperty('_value') ? op._value : op.value;\n      /* eslint-disable eqeqeq */\n      op.selected = multi ? indexOf$1(value, val) > -1 : looseEqual(value, val);\n      /* eslint-enable eqeqeq */\n    }\n  },\n\n  unbind: function unbind() {\n    /* istanbul ignore next */\n    this.vm.$off('hook:attached', this.forceUpdate);\n  }\n};\n\n/**\n * Get select value\n *\n * @param {SelectElement} el\n * @param {Boolean} multi\n * @param {Boolean} init\n * @return {Array|*}\n */\n\nfunction getValue(el, multi, init) {\n  var res = multi ? [] : null;\n  var op, val, selected;\n  for (var i = 0, l = el.options.length; i < l; i++) {\n    op = el.options[i];\n    selected = init ? op.hasAttribute('selected') : op.selected;\n    if (selected) {\n      val = op.hasOwnProperty('_value') ? op._value : op.value;\n      if (multi) {\n        res.push(val);\n      } else {\n        return val;\n      }\n    }\n  }\n  return res;\n}\n\n/**\n * Native Array.indexOf uses strict equal, but in this\n * case we need to match string/numbers with custom equal.\n *\n * @param {Array} arr\n * @param {*} val\n */\n\nfunction indexOf$1(arr, val) {\n  var i = arr.length;\n  while (i--) {\n    if (looseEqual(arr[i], val)) {\n      return i;\n    }\n  }\n  return -1;\n}\n\nvar checkbox = {\n\n  bind: function bind() {\n    var self = this;\n    var el = this.el;\n\n    this.getValue = function () {\n      return el.hasOwnProperty('_value') ? el._value : self.params.number ? toNumber(el.value) : el.value;\n    };\n\n    function getBooleanValue() {\n      var val = el.checked;\n      if (val && el.hasOwnProperty('_trueValue')) {\n        return el._trueValue;\n      }\n      if (!val && el.hasOwnProperty('_falseValue')) {\n        return el._falseValue;\n      }\n      return val;\n    }\n\n    this.listener = function () {\n      var model = self._watcher.get();\n      if (isArray(model)) {\n        var val = self.getValue();\n        var i = indexOf(model, val);\n        if (el.checked) {\n          if (i < 0) {\n            self.set(model.concat(val));\n          }\n        } else if (i > -1) {\n          self.set(model.slice(0, i).concat(model.slice(i + 1)));\n        }\n      } else {\n        self.set(getBooleanValue());\n      }\n    };\n\n    this.on('change', this.listener);\n    if (el.hasAttribute('checked')) {\n      this.afterBind = this.listener;\n    }\n  },\n\n  update: function update(value) {\n    var el = this.el;\n    if (isArray(value)) {\n      el.checked = indexOf(value, this.getValue()) > -1;\n    } else {\n      if (el.hasOwnProperty('_trueValue')) {\n        el.checked = looseEqual(value, el._trueValue);\n      } else {\n        el.checked = !!value;\n      }\n    }\n  }\n};\n\nvar handlers = {\n  text: text$2,\n  radio: radio,\n  select: select,\n  checkbox: checkbox\n};\n\nvar model = {\n\n  priority: MODEL,\n  twoWay: true,\n  handlers: handlers,\n  params: ['lazy', 'number', 'debounce'],\n\n  /**\n   * Possible elements:\n   *   <select>\n   *   <textarea>\n   *   <input type=\"*\">\n   *     - text\n   *     - checkbox\n   *     - radio\n   *     - number\n   */\n\n  bind: function bind() {\n    // friendly warning...\n    this.checkFilters();\n    if (this.hasRead && !this.hasWrite) {\n      'production' !== 'production' && warn('It seems you are using a read-only filter with ' + 'v-model=\"' + this.descriptor.raw + '\". ' + 'You might want to use a two-way filter to ensure correct behavior.', this.vm);\n    }\n    var el = this.el;\n    var tag = el.tagName;\n    var handler;\n    if (tag === 'INPUT') {\n      handler = handlers[el.type] || handlers.text;\n    } else if (tag === 'SELECT') {\n      handler = handlers.select;\n    } else if (tag === 'TEXTAREA') {\n      handler = handlers.text;\n    } else {\n      'production' !== 'production' && warn('v-model does not support element type: ' + tag, this.vm);\n      return;\n    }\n    el.__v_model = this;\n    handler.bind.call(this);\n    this.update = handler.update;\n    this._unbind = handler.unbind;\n  },\n\n  /**\n   * Check read/write filter stats.\n   */\n\n  checkFilters: function checkFilters() {\n    var filters = this.filters;\n    if (!filters) return;\n    var i = filters.length;\n    while (i--) {\n      var filter = resolveAsset(this.vm.$options, 'filters', filters[i].name);\n      if (typeof filter === 'function' || filter.read) {\n        this.hasRead = true;\n      }\n      if (filter.write) {\n        this.hasWrite = true;\n      }\n    }\n  },\n\n  unbind: function unbind() {\n    this.el.__v_model = null;\n    this._unbind && this._unbind();\n  }\n};\n\n// keyCode aliases\nvar keyCodes = {\n  esc: 27,\n  tab: 9,\n  enter: 13,\n  space: 32,\n  'delete': [8, 46],\n  up: 38,\n  left: 37,\n  right: 39,\n  down: 40\n};\n\nfunction keyFilter(handler, keys) {\n  var codes = keys.map(function (key) {\n    var charCode = key.charCodeAt(0);\n    if (charCode > 47 && charCode < 58) {\n      return parseInt(key, 10);\n    }\n    if (key.length === 1) {\n      charCode = key.toUpperCase().charCodeAt(0);\n      if (charCode > 64 && charCode < 91) {\n        return charCode;\n      }\n    }\n    return keyCodes[key];\n  });\n  codes = [].concat.apply([], codes);\n  return function keyHandler(e) {\n    if (codes.indexOf(e.keyCode) > -1) {\n      return handler.call(this, e);\n    }\n  };\n}\n\nfunction stopFilter(handler) {\n  return function stopHandler(e) {\n    e.stopPropagation();\n    return handler.call(this, e);\n  };\n}\n\nfunction preventFilter(handler) {\n  return function preventHandler(e) {\n    e.preventDefault();\n    return handler.call(this, e);\n  };\n}\n\nfunction selfFilter(handler) {\n  return function selfHandler(e) {\n    if (e.target === e.currentTarget) {\n      return handler.call(this, e);\n    }\n  };\n}\n\nvar on$1 = {\n\n  priority: ON,\n  acceptStatement: true,\n  keyCodes: keyCodes,\n\n  bind: function bind() {\n    // deal with iframes\n    if (this.el.tagName === 'IFRAME' && this.arg !== 'load') {\n      var self = this;\n      this.iframeBind = function () {\n        on(self.el.contentWindow, self.arg, self.handler, self.modifiers.capture);\n      };\n      this.on('load', this.iframeBind);\n    }\n  },\n\n  update: function update(handler) {\n    // stub a noop for v-on with no value,\n    // e.g. @mousedown.prevent\n    if (!this.descriptor.raw) {\n      handler = function () {};\n    }\n\n    if (typeof handler !== 'function') {\n      'production' !== 'production' && warn('v-on:' + this.arg + '=\"' + this.expression + '\" expects a function value, ' + 'got ' + handler, this.vm);\n      return;\n    }\n\n    // apply modifiers\n    if (this.modifiers.stop) {\n      handler = stopFilter(handler);\n    }\n    if (this.modifiers.prevent) {\n      handler = preventFilter(handler);\n    }\n    if (this.modifiers.self) {\n      handler = selfFilter(handler);\n    }\n    // key filter\n    var keys = Object.keys(this.modifiers).filter(function (key) {\n      return key !== 'stop' && key !== 'prevent' && key !== 'self' && key !== 'capture';\n    });\n    if (keys.length) {\n      handler = keyFilter(handler, keys);\n    }\n\n    this.reset();\n    this.handler = handler;\n\n    if (this.iframeBind) {\n      this.iframeBind();\n    } else {\n      on(this.el, this.arg, this.handler, this.modifiers.capture);\n    }\n  },\n\n  reset: function reset() {\n    var el = this.iframeBind ? this.el.contentWindow : this.el;\n    if (this.handler) {\n      off(el, this.arg, this.handler);\n    }\n  },\n\n  unbind: function unbind() {\n    this.reset();\n  }\n};\n\nvar prefixes = ['-webkit-', '-moz-', '-ms-'];\nvar camelPrefixes = ['Webkit', 'Moz', 'ms'];\nvar importantRE = /!important;?$/;\nvar propCache = Object.create(null);\n\nvar testEl = null;\n\nvar style = {\n\n  deep: true,\n\n  update: function update(value) {\n    if (typeof value === 'string') {\n      this.el.style.cssText = value;\n    } else if (isArray(value)) {\n      this.handleObject(value.reduce(extend, {}));\n    } else {\n      this.handleObject(value || {});\n    }\n  },\n\n  handleObject: function handleObject(value) {\n    // cache object styles so that only changed props\n    // are actually updated.\n    var cache = this.cache || (this.cache = {});\n    var name, val;\n    for (name in cache) {\n      if (!(name in value)) {\n        this.handleSingle(name, null);\n        delete cache[name];\n      }\n    }\n    for (name in value) {\n      val = value[name];\n      if (val !== cache[name]) {\n        cache[name] = val;\n        this.handleSingle(name, val);\n      }\n    }\n  },\n\n  handleSingle: function handleSingle(prop, value) {\n    prop = normalize(prop);\n    if (!prop) return; // unsupported prop\n    // cast possible numbers/booleans into strings\n    if (value != null) value += '';\n    if (value) {\n      var isImportant = importantRE.test(value) ? 'important' : '';\n      if (isImportant) {\n        /* istanbul ignore if */\n        if ('production' !== 'production') {}\n        value = value.replace(importantRE, '').trim();\n        this.el.style.setProperty(prop.kebab, value, isImportant);\n      } else {\n        this.el.style[prop.camel] = value;\n      }\n    } else {\n      this.el.style[prop.camel] = '';\n    }\n  }\n\n};\n\n/**\n * Normalize a CSS property name.\n * - cache result\n * - auto prefix\n * - camelCase -> dash-case\n *\n * @param {String} prop\n * @return {String}\n */\n\nfunction normalize(prop) {\n  if (propCache[prop]) {\n    return propCache[prop];\n  }\n  var res = prefix(prop);\n  propCache[prop] = propCache[res] = res;\n  return res;\n}\n\n/**\n * Auto detect the appropriate prefix for a CSS property.\n * https://gist.github.com/paulirish/523692\n *\n * @param {String} prop\n * @return {String}\n */\n\nfunction prefix(prop) {\n  prop = hyphenate(prop);\n  var camel = camelize(prop);\n  var upper = camel.charAt(0).toUpperCase() + camel.slice(1);\n  if (!testEl) {\n    testEl = document.createElement('div');\n  }\n  var i = prefixes.length;\n  var prefixed;\n  if (camel !== 'filter' && camel in testEl.style) {\n    return {\n      kebab: prop,\n      camel: camel\n    };\n  }\n  while (i--) {\n    prefixed = camelPrefixes[i] + upper;\n    if (prefixed in testEl.style) {\n      return {\n        kebab: prefixes[i] + prop,\n        camel: prefixed\n      };\n    }\n  }\n}\n\n// xlink\nvar xlinkNS = 'http://www.w3.org/1999/xlink';\nvar xlinkRE = /^xlink:/;\n\n// check for attributes that prohibit interpolations\nvar disallowedInterpAttrRE = /^v-|^:|^@|^(?:is|transition|transition-mode|debounce|track-by|stagger|enter-stagger|leave-stagger)$/;\n// these attributes should also set their corresponding properties\n// because they only affect the initial state of the element\nvar attrWithPropsRE = /^(?:value|checked|selected|muted)$/;\n// these attributes expect enumrated values of \"true\" or \"false\"\n// but are not boolean attributes\nvar enumeratedAttrRE = /^(?:draggable|contenteditable|spellcheck)$/;\n\n// these attributes should set a hidden property for\n// binding v-model to object values\nvar modelProps = {\n  value: '_value',\n  'true-value': '_trueValue',\n  'false-value': '_falseValue'\n};\n\nvar bind$1 = {\n\n  priority: BIND,\n\n  bind: function bind() {\n    var attr = this.arg;\n    var tag = this.el.tagName;\n    // should be deep watch on object mode\n    if (!attr) {\n      this.deep = true;\n    }\n    // handle interpolation bindings\n    var descriptor = this.descriptor;\n    var tokens = descriptor.interp;\n    if (tokens) {\n      // handle interpolations with one-time tokens\n      if (descriptor.hasOneTime) {\n        this.expression = tokensToExp(tokens, this._scope || this.vm);\n      }\n\n      // only allow binding on native attributes\n      if (disallowedInterpAttrRE.test(attr) || attr === 'name' && (tag === 'PARTIAL' || tag === 'SLOT')) {\n        'production' !== 'production' && warn(attr + '=\"' + descriptor.raw + '\": ' + 'attribute interpolation is not allowed in Vue.js ' + 'directives and special attributes.', this.vm);\n        this.el.removeAttribute(attr);\n        this.invalid = true;\n      }\n\n      /* istanbul ignore if */\n      if ('production' !== 'production') {}\n    }\n  },\n\n  update: function update(value) {\n    if (this.invalid) {\n      return;\n    }\n    var attr = this.arg;\n    if (this.arg) {\n      this.handleSingle(attr, value);\n    } else {\n      this.handleObject(value || {});\n    }\n  },\n\n  // share object handler with v-bind:class\n  handleObject: style.handleObject,\n\n  handleSingle: function handleSingle(attr, value) {\n    var el = this.el;\n    var interp = this.descriptor.interp;\n    if (this.modifiers.camel) {\n      attr = camelize(attr);\n    }\n    if (!interp && attrWithPropsRE.test(attr) && attr in el) {\n      var attrValue = attr === 'value' ? value == null // IE9 will set input.value to \"null\" for null...\n      ? '' : value : value;\n\n      if (el[attr] !== attrValue) {\n        el[attr] = attrValue;\n      }\n    }\n    // set model props\n    var modelProp = modelProps[attr];\n    if (!interp && modelProp) {\n      el[modelProp] = value;\n      // update v-model if present\n      var model = el.__v_model;\n      if (model) {\n        model.listener();\n      }\n    }\n    // do not set value attribute for textarea\n    if (attr === 'value' && el.tagName === 'TEXTAREA') {\n      el.removeAttribute(attr);\n      return;\n    }\n    // update attribute\n    if (enumeratedAttrRE.test(attr)) {\n      el.setAttribute(attr, value ? 'true' : 'false');\n    } else if (value != null && value !== false) {\n      if (attr === 'class') {\n        // handle edge case #1960:\n        // class interpolation should not overwrite Vue transition class\n        if (el.__v_trans) {\n          value += ' ' + el.__v_trans.id + '-transition';\n        }\n        setClass(el, value);\n      } else if (xlinkRE.test(attr)) {\n        el.setAttributeNS(xlinkNS, attr, value === true ? '' : value);\n      } else {\n        el.setAttribute(attr, value === true ? '' : value);\n      }\n    } else {\n      el.removeAttribute(attr);\n    }\n  }\n};\n\nvar el = {\n\n  priority: EL,\n\n  bind: function bind() {\n    /* istanbul ignore if */\n    if (!this.arg) {\n      return;\n    }\n    var id = this.id = camelize(this.arg);\n    var refs = (this._scope || this.vm).$els;\n    if (hasOwn(refs, id)) {\n      refs[id] = this.el;\n    } else {\n      defineReactive(refs, id, this.el);\n    }\n  },\n\n  unbind: function unbind() {\n    var refs = (this._scope || this.vm).$els;\n    if (refs[this.id] === this.el) {\n      refs[this.id] = null;\n    }\n  }\n};\n\nvar ref = {\n  bind: function bind() {\n    'production' !== 'production' && warn('v-ref:' + this.arg + ' must be used on a child ' + 'component. Found on <' + this.el.tagName.toLowerCase() + '>.', this.vm);\n  }\n};\n\nvar cloak = {\n  bind: function bind() {\n    var el = this.el;\n    this.vm.$once('pre-hook:compiled', function () {\n      el.removeAttribute('v-cloak');\n    });\n  }\n};\n\n// logic control\n// two-way binding\n// event handling\n// attributes\n// ref & el\n// cloak\n// must export plain object\nvar directives = {\n  text: text$1,\n  html: html,\n  'for': vFor,\n  'if': vIf,\n  show: show,\n  model: model,\n  on: on$1,\n  bind: bind$1,\n  el: el,\n  ref: ref,\n  cloak: cloak\n};\n\nvar vClass = {\n\n  deep: true,\n\n  update: function update(value) {\n    if (!value) {\n      this.cleanup();\n    } else if (typeof value === 'string') {\n      this.setClass(value.trim().split(/\\s+/));\n    } else {\n      this.setClass(normalize$1(value));\n    }\n  },\n\n  setClass: function setClass(value) {\n    this.cleanup(value);\n    for (var i = 0, l = value.length; i < l; i++) {\n      var val = value[i];\n      if (val) {\n        apply(this.el, val, addClass);\n      }\n    }\n    this.prevKeys = value;\n  },\n\n  cleanup: function cleanup(value) {\n    var prevKeys = this.prevKeys;\n    if (!prevKeys) return;\n    var i = prevKeys.length;\n    while (i--) {\n      var key = prevKeys[i];\n      if (!value || value.indexOf(key) < 0) {\n        apply(this.el, key, removeClass);\n      }\n    }\n  }\n};\n\n/**\n * Normalize objects and arrays (potentially containing objects)\n * into array of strings.\n *\n * @param {Object|Array<String|Object>} value\n * @return {Array<String>}\n */\n\nfunction normalize$1(value) {\n  var res = [];\n  if (isArray(value)) {\n    for (var i = 0, l = value.length; i < l; i++) {\n      var _key = value[i];\n      if (_key) {\n        if (typeof _key === 'string') {\n          res.push(_key);\n        } else {\n          for (var k in _key) {\n            if (_key[k]) res.push(k);\n          }\n        }\n      }\n    }\n  } else if (isObject(value)) {\n    for (var key in value) {\n      if (value[key]) res.push(key);\n    }\n  }\n  return res;\n}\n\n/**\n * Add or remove a class/classes on an element\n *\n * @param {Element} el\n * @param {String} key The class name. This may or may not\n *                     contain a space character, in such a\n *                     case we'll deal with multiple class\n *                     names at once.\n * @param {Function} fn\n */\n\nfunction apply(el, key, fn) {\n  key = key.trim();\n  if (key.indexOf(' ') === -1) {\n    fn(el, key);\n    return;\n  }\n  // The key contains one or more space characters.\n  // Since a class name doesn't accept such characters, we\n  // treat it as multiple classes.\n  var keys = key.split(/\\s+/);\n  for (var i = 0, l = keys.length; i < l; i++) {\n    fn(el, keys[i]);\n  }\n}\n\nvar component = {\n\n  priority: COMPONENT,\n\n  params: ['keep-alive', 'transition-mode', 'inline-template'],\n\n  /**\n   * Setup. Two possible usages:\n   *\n   * - static:\n   *   <comp> or <div v-component=\"comp\">\n   *\n   * - dynamic:\n   *   <component :is=\"view\">\n   */\n\n  bind: function bind() {\n    if (!this.el.__vue__) {\n      // keep-alive cache\n      this.keepAlive = this.params.keepAlive;\n      if (this.keepAlive) {\n        this.cache = {};\n      }\n      // check inline-template\n      if (this.params.inlineTemplate) {\n        // extract inline template as a DocumentFragment\n        this.inlineTemplate = extractContent(this.el, true);\n      }\n      // component resolution related state\n      this.pendingComponentCb = this.Component = null;\n      // transition related state\n      this.pendingRemovals = 0;\n      this.pendingRemovalCb = null;\n      // create a ref anchor\n      this.anchor = createAnchor('v-component');\n      replace(this.el, this.anchor);\n      // remove is attribute.\n      // this is removed during compilation, but because compilation is\n      // cached, when the component is used elsewhere this attribute\n      // will remain at link time.\n      this.el.removeAttribute('is');\n      this.el.removeAttribute(':is');\n      // remove ref, same as above\n      if (this.descriptor.ref) {\n        this.el.removeAttribute('v-ref:' + hyphenate(this.descriptor.ref));\n      }\n      // if static, build right now.\n      if (this.literal) {\n        this.setComponent(this.expression);\n      }\n    } else {\n      'production' !== 'production' && warn('cannot mount component \"' + this.expression + '\" ' + 'on already mounted element: ' + this.el);\n    }\n  },\n\n  /**\n   * Public update, called by the watcher in the dynamic\n   * literal scenario, e.g. <component :is=\"view\">\n   */\n\n  update: function update(value) {\n    if (!this.literal) {\n      this.setComponent(value);\n    }\n  },\n\n  /**\n   * Switch dynamic components. May resolve the component\n   * asynchronously, and perform transition based on\n   * specified transition mode. Accepts a few additional\n   * arguments specifically for vue-router.\n   *\n   * The callback is called when the full transition is\n   * finished.\n   *\n   * @param {String} value\n   * @param {Function} [cb]\n   */\n\n  setComponent: function setComponent(value, cb) {\n    this.invalidatePending();\n    if (!value) {\n      // just remove current\n      this.unbuild(true);\n      this.remove(this.childVM, cb);\n      this.childVM = null;\n    } else {\n      var self = this;\n      this.resolveComponent(value, function () {\n        self.mountComponent(cb);\n      });\n    }\n  },\n\n  /**\n   * Resolve the component constructor to use when creating\n   * the child vm.\n   *\n   * @param {String|Function} value\n   * @param {Function} cb\n   */\n\n  resolveComponent: function resolveComponent(value, cb) {\n    var self = this;\n    this.pendingComponentCb = cancellable(function (Component) {\n      self.ComponentName = Component.options.name || (typeof value === 'string' ? value : null);\n      self.Component = Component;\n      cb();\n    });\n    this.vm._resolveComponent(value, this.pendingComponentCb);\n  },\n\n  /**\n   * Create a new instance using the current constructor and\n   * replace the existing instance. This method doesn't care\n   * whether the new component and the old one are actually\n   * the same.\n   *\n   * @param {Function} [cb]\n   */\n\n  mountComponent: function mountComponent(cb) {\n    // actual mount\n    this.unbuild(true);\n    var self = this;\n    var activateHooks = this.Component.options.activate;\n    var cached = this.getCached();\n    var newComponent = this.build();\n    if (activateHooks && !cached) {\n      this.waitingFor = newComponent;\n      callActivateHooks(activateHooks, newComponent, function () {\n        if (self.waitingFor !== newComponent) {\n          return;\n        }\n        self.waitingFor = null;\n        self.transition(newComponent, cb);\n      });\n    } else {\n      // update ref for kept-alive component\n      if (cached) {\n        newComponent._updateRef();\n      }\n      this.transition(newComponent, cb);\n    }\n  },\n\n  /**\n   * When the component changes or unbinds before an async\n   * constructor is resolved, we need to invalidate its\n   * pending callback.\n   */\n\n  invalidatePending: function invalidatePending() {\n    if (this.pendingComponentCb) {\n      this.pendingComponentCb.cancel();\n      this.pendingComponentCb = null;\n    }\n  },\n\n  /**\n   * Instantiate/insert a new child vm.\n   * If keep alive and has cached instance, insert that\n   * instance; otherwise build a new one and cache it.\n   *\n   * @param {Object} [extraOptions]\n   * @return {Vue} - the created instance\n   */\n\n  build: function build(extraOptions) {\n    var cached = this.getCached();\n    if (cached) {\n      return cached;\n    }\n    if (this.Component) {\n      // default options\n      var options = {\n        name: this.ComponentName,\n        el: cloneNode(this.el),\n        template: this.inlineTemplate,\n        // make sure to add the child with correct parent\n        // if this is a transcluded component, its parent\n        // should be the transclusion host.\n        parent: this._host || this.vm,\n        // if no inline-template, then the compiled\n        // linker can be cached for better performance.\n        _linkerCachable: !this.inlineTemplate,\n        _ref: this.descriptor.ref,\n        _asComponent: true,\n        _isRouterView: this._isRouterView,\n        // if this is a transcluded component, context\n        // will be the common parent vm of this instance\n        // and its host.\n        _context: this.vm,\n        // if this is inside an inline v-for, the scope\n        // will be the intermediate scope created for this\n        // repeat fragment. this is used for linking props\n        // and container directives.\n        _scope: this._scope,\n        // pass in the owner fragment of this component.\n        // this is necessary so that the fragment can keep\n        // track of its contained components in order to\n        // call attach/detach hooks for them.\n        _frag: this._frag\n      };\n      // extra options\n      // in 1.0.0 this is used by vue-router only\n      /* istanbul ignore if */\n      if (extraOptions) {\n        extend(options, extraOptions);\n      }\n      var child = new this.Component(options);\n      if (this.keepAlive) {\n        this.cache[this.Component.cid] = child;\n      }\n      /* istanbul ignore if */\n      if ('production' !== 'production' && this.el.hasAttribute('transition') && child._isFragment) {}\n      return child;\n    }\n  },\n\n  /**\n   * Try to get a cached instance of the current component.\n   *\n   * @return {Vue|undefined}\n   */\n\n  getCached: function getCached() {\n    return this.keepAlive && this.cache[this.Component.cid];\n  },\n\n  /**\n   * Teardown the current child, but defers cleanup so\n   * that we can separate the destroy and removal steps.\n   *\n   * @param {Boolean} defer\n   */\n\n  unbuild: function unbuild(defer) {\n    if (this.waitingFor) {\n      if (!this.keepAlive) {\n        this.waitingFor.$destroy();\n      }\n      this.waitingFor = null;\n    }\n    var child = this.childVM;\n    if (!child || this.keepAlive) {\n      if (child) {\n        // remove ref\n        child._inactive = true;\n        child._updateRef(true);\n      }\n      return;\n    }\n    // the sole purpose of `deferCleanup` is so that we can\n    // \"deactivate\" the vm right now and perform DOM removal\n    // later.\n    child.$destroy(false, defer);\n  },\n\n  /**\n   * Remove current destroyed child and manually do\n   * the cleanup after removal.\n   *\n   * @param {Function} cb\n   */\n\n  remove: function remove(child, cb) {\n    var keepAlive = this.keepAlive;\n    if (child) {\n      // we may have a component switch when a previous\n      // component is still being transitioned out.\n      // we want to trigger only one lastest insertion cb\n      // when the existing transition finishes. (#1119)\n      this.pendingRemovals++;\n      this.pendingRemovalCb = cb;\n      var self = this;\n      child.$remove(function () {\n        self.pendingRemovals--;\n        if (!keepAlive) child._cleanup();\n        if (!self.pendingRemovals && self.pendingRemovalCb) {\n          self.pendingRemovalCb();\n          self.pendingRemovalCb = null;\n        }\n      });\n    } else if (cb) {\n      cb();\n    }\n  },\n\n  /**\n   * Actually swap the components, depending on the\n   * transition mode. Defaults to simultaneous.\n   *\n   * @param {Vue} target\n   * @param {Function} [cb]\n   */\n\n  transition: function transition(target, cb) {\n    var self = this;\n    var current = this.childVM;\n    // for devtool inspection\n    if (current) current._inactive = true;\n    target._inactive = false;\n    this.childVM = target;\n    switch (self.params.transitionMode) {\n      case 'in-out':\n        target.$before(self.anchor, function () {\n          self.remove(current, cb);\n        });\n        break;\n      case 'out-in':\n        self.remove(current, function () {\n          target.$before(self.anchor, cb);\n        });\n        break;\n      default:\n        self.remove(current);\n        target.$before(self.anchor, cb);\n    }\n  },\n\n  /**\n   * Unbind.\n   */\n\n  unbind: function unbind() {\n    this.invalidatePending();\n    // Do not defer cleanup when unbinding\n    this.unbuild();\n    // destroy all keep-alive cached instances\n    if (this.cache) {\n      for (var key in this.cache) {\n        this.cache[key].$destroy();\n      }\n      this.cache = null;\n    }\n  }\n};\n\n/**\n * Call activate hooks in order (asynchronous)\n *\n * @param {Array} hooks\n * @param {Vue} vm\n * @param {Function} cb\n */\n\nfunction callActivateHooks(hooks, vm, cb) {\n  var total = hooks.length;\n  var called = 0;\n  hooks[0].call(vm, next);\n  function next() {\n    if (++called >= total) {\n      cb();\n    } else {\n      hooks[called].call(vm, next);\n    }\n  }\n}\n\nvar propBindingModes = config._propBindingModes;\nvar empty = {};\n\n// regexes\nvar identRE$1 = /^[$_a-zA-Z]+[\\w$]*$/;\nvar settablePathRE = /^[A-Za-z_$][\\w$]*(\\.[A-Za-z_$][\\w$]*|\\[[^\\[\\]]+\\])*$/;\n\n/**\n * Compile props on a root element and return\n * a props link function.\n *\n * @param {Element|DocumentFragment} el\n * @param {Array} propOptions\n * @param {Vue} vm\n * @return {Function} propsLinkFn\n */\n\nfunction compileProps(el, propOptions, vm) {\n  var props = [];\n  var propsData = vm.$options.propsData;\n  var names = Object.keys(propOptions);\n  var i = names.length;\n  var options, name, attr, value, path, parsed, prop;\n  while (i--) {\n    name = names[i];\n    options = propOptions[name] || empty;\n\n    if ('production' !== 'production' && name === '$data') {}\n\n    // props could contain dashes, which will be\n    // interpreted as minus calculations by the parser\n    // so we need to camelize the path here\n    path = camelize(name);\n    if (!identRE$1.test(path)) {\n      'production' !== 'production' && warn('Invalid prop key: \"' + name + '\". Prop keys ' + 'must be valid identifiers.', vm);\n      continue;\n    }\n\n    prop = {\n      name: name,\n      path: path,\n      options: options,\n      mode: propBindingModes.ONE_WAY,\n      raw: null\n    };\n\n    attr = hyphenate(name);\n    // first check dynamic version\n    if ((value = getBindAttr(el, attr)) === null) {\n      if ((value = getBindAttr(el, attr + '.sync')) !== null) {\n        prop.mode = propBindingModes.TWO_WAY;\n      } else if ((value = getBindAttr(el, attr + '.once')) !== null) {\n        prop.mode = propBindingModes.ONE_TIME;\n      }\n    }\n    if (value !== null) {\n      // has dynamic binding!\n      prop.raw = value;\n      parsed = parseDirective(value);\n      value = parsed.expression;\n      prop.filters = parsed.filters;\n      // check binding type\n      if (isLiteral(value) && !parsed.filters) {\n        // for expressions containing literal numbers and\n        // booleans, there's no need to setup a prop binding,\n        // so we can optimize them as a one-time set.\n        prop.optimizedLiteral = true;\n      } else {\n        prop.dynamic = true;\n        // check non-settable path for two-way bindings\n        if ('production' !== 'production' && prop.mode === propBindingModes.TWO_WAY && !settablePathRE.test(value)) {}\n      }\n      prop.parentPath = value;\n\n      // warn required two-way\n      if ('production' !== 'production' && options.twoWay && prop.mode !== propBindingModes.TWO_WAY) {}\n    } else if ((value = getAttr(el, attr)) !== null) {\n      // has literal binding!\n      prop.raw = value;\n    } else if (propsData && (value = propsData[name] || propsData[path]) !== null) {\n      // has propsData\n      prop.raw = value;\n    } else if ('production' !== 'production') {}\n    // push prop\n    props.push(prop);\n  }\n  return makePropsLinkFn(props);\n}\n\n/**\n * Build a function that applies props to a vm.\n *\n * @param {Array} props\n * @return {Function} propsLinkFn\n */\n\nfunction makePropsLinkFn(props) {\n  return function propsLinkFn(vm, scope) {\n    // store resolved props info\n    vm._props = {};\n    var inlineProps = vm.$options.propsData;\n    var i = props.length;\n    var prop, path, options, value, raw;\n    while (i--) {\n      prop = props[i];\n      raw = prop.raw;\n      path = prop.path;\n      options = prop.options;\n      vm._props[path] = prop;\n      if (inlineProps && hasOwn(inlineProps, path)) {\n        initProp(vm, prop, inlineProps[path]);\n      }if (raw === null) {\n        // initialize absent prop\n        initProp(vm, prop, undefined);\n      } else if (prop.dynamic) {\n        // dynamic prop\n        if (prop.mode === propBindingModes.ONE_TIME) {\n          // one time binding\n          value = (scope || vm._context || vm).$get(prop.parentPath);\n          initProp(vm, prop, value);\n        } else {\n          if (vm._context) {\n            // dynamic binding\n            vm._bindDir({\n              name: 'prop',\n              def: propDef,\n              prop: prop\n            }, null, null, scope); // el, host, scope\n          } else {\n              // root instance\n              initProp(vm, prop, vm.$get(prop.parentPath));\n            }\n        }\n      } else if (prop.optimizedLiteral) {\n        // optimized literal, cast it and just set once\n        var stripped = stripQuotes(raw);\n        value = stripped === raw ? toBoolean(toNumber(raw)) : stripped;\n        initProp(vm, prop, value);\n      } else {\n        // string literal, but we need to cater for\n        // Boolean props with no value, or with same\n        // literal value (e.g. disabled=\"disabled\")\n        // see https://github.com/vuejs/vue-loader/issues/182\n        value = options.type === Boolean && (raw === '' || raw === hyphenate(prop.name)) ? true : raw;\n        initProp(vm, prop, value);\n      }\n    }\n  };\n}\n\n/**\n * Process a prop with a rawValue, applying necessary coersions,\n * default values & assertions and call the given callback with\n * processed value.\n *\n * @param {Vue} vm\n * @param {Object} prop\n * @param {*} rawValue\n * @param {Function} fn\n */\n\nfunction processPropValue(vm, prop, rawValue, fn) {\n  var isSimple = prop.dynamic && isSimplePath(prop.parentPath);\n  var value = rawValue;\n  if (value === undefined) {\n    value = getPropDefaultValue(vm, prop);\n  }\n  value = coerceProp(prop, value, vm);\n  var coerced = value !== rawValue;\n  if (!assertProp(prop, value, vm)) {\n    value = undefined;\n  }\n  if (isSimple && !coerced) {\n    withoutConversion(function () {\n      fn(value);\n    });\n  } else {\n    fn(value);\n  }\n}\n\n/**\n * Set a prop's initial value on a vm and its data object.\n *\n * @param {Vue} vm\n * @param {Object} prop\n * @param {*} value\n */\n\nfunction initProp(vm, prop, value) {\n  processPropValue(vm, prop, value, function (value) {\n    defineReactive(vm, prop.path, value);\n  });\n}\n\n/**\n * Update a prop's value on a vm.\n *\n * @param {Vue} vm\n * @param {Object} prop\n * @param {*} value\n */\n\nfunction updateProp(vm, prop, value) {\n  processPropValue(vm, prop, value, function (value) {\n    vm[prop.path] = value;\n  });\n}\n\n/**\n * Get the default value of a prop.\n *\n * @param {Vue} vm\n * @param {Object} prop\n * @return {*}\n */\n\nfunction getPropDefaultValue(vm, prop) {\n  // no default, return undefined\n  var options = prop.options;\n  if (!hasOwn(options, 'default')) {\n    // absent boolean value defaults to false\n    return options.type === Boolean ? false : undefined;\n  }\n  var def = options['default'];\n  // warn against non-factory defaults for Object & Array\n  if (isObject(def)) {\n    'production' !== 'production' && warn('Invalid default value for prop \"' + prop.name + '\": ' + 'Props with type Object/Array must use a factory function ' + 'to return the default value.', vm);\n  }\n  // call factory function for non-Function types\n  return typeof def === 'function' && options.type !== Function ? def.call(vm) : def;\n}\n\n/**\n * Assert whether a prop is valid.\n *\n * @param {Object} prop\n * @param {*} value\n * @param {Vue} vm\n */\n\nfunction assertProp(prop, value, vm) {\n  if (!prop.options.required && ( // non-required\n  prop.raw === null || // abscent\n  value == null) // null or undefined\n  ) {\n      return true;\n    }\n  var options = prop.options;\n  var type = options.type;\n  var valid = !type;\n  var expectedTypes = [];\n  if (type) {\n    if (!isArray(type)) {\n      type = [type];\n    }\n    for (var i = 0; i < type.length && !valid; i++) {\n      var assertedType = assertType(value, type[i]);\n      expectedTypes.push(assertedType.expectedType);\n      valid = assertedType.valid;\n    }\n  }\n  if (!valid) {\n    if ('production' !== 'production') {}\n    return false;\n  }\n  var validator = options.validator;\n  if (validator) {\n    if (!validator(value)) {\n      'production' !== 'production' && warn('Invalid prop: custom validator check failed for prop \"' + prop.name + '\".', vm);\n      return false;\n    }\n  }\n  return true;\n}\n\n/**\n * Force parsing value with coerce option.\n *\n * @param {*} value\n * @param {Object} options\n * @return {*}\n */\n\nfunction coerceProp(prop, value, vm) {\n  var coerce = prop.options.coerce;\n  if (!coerce) {\n    return value;\n  }\n  if (typeof coerce === 'function') {\n    return coerce(value);\n  } else {\n    'production' !== 'production' && warn('Invalid coerce for prop \"' + prop.name + '\": expected function, got ' + typeof coerce + '.', vm);\n    return value;\n  }\n}\n\n/**\n * Assert the type of a value\n *\n * @param {*} value\n * @param {Function} type\n * @return {Object}\n */\n\nfunction assertType(value, type) {\n  var valid;\n  var expectedType;\n  if (type === String) {\n    expectedType = 'string';\n    valid = typeof value === expectedType;\n  } else if (type === Number) {\n    expectedType = 'number';\n    valid = typeof value === expectedType;\n  } else if (type === Boolean) {\n    expectedType = 'boolean';\n    valid = typeof value === expectedType;\n  } else if (type === Function) {\n    expectedType = 'function';\n    valid = typeof value === expectedType;\n  } else if (type === Object) {\n    expectedType = 'object';\n    valid = isPlainObject(value);\n  } else if (type === Array) {\n    expectedType = 'array';\n    valid = isArray(value);\n  } else {\n    valid = value instanceof type;\n  }\n  return {\n    valid: valid,\n    expectedType: expectedType\n  };\n}\n\nvar bindingModes = config._propBindingModes;\n\nvar propDef = {\n\n  bind: function bind() {\n    var child = this.vm;\n    var parent = child._context;\n    // passed in from compiler directly\n    var prop = this.descriptor.prop;\n    var childKey = prop.path;\n    var parentKey = prop.parentPath;\n    var twoWay = prop.mode === bindingModes.TWO_WAY;\n\n    var parentWatcher = this.parentWatcher = new Watcher(parent, parentKey, function (val) {\n      updateProp(child, prop, val);\n    }, {\n      twoWay: twoWay,\n      filters: prop.filters,\n      // important: props need to be observed on the\n      // v-for scope if present\n      scope: this._scope\n    });\n\n    // set the child initial value.\n    initProp(child, prop, parentWatcher.value);\n\n    // setup two-way binding\n    if (twoWay) {\n      // important: defer the child watcher creation until\n      // the created hook (after data observation)\n      var self = this;\n      child.$once('pre-hook:created', function () {\n        self.childWatcher = new Watcher(child, childKey, function (val) {\n          parentWatcher.set(val);\n        }, {\n          // ensure sync upward before parent sync down.\n          // this is necessary in cases e.g. the child\n          // mutates a prop array, then replaces it. (#1683)\n          sync: true\n        });\n      });\n    }\n  },\n\n  unbind: function unbind() {\n    this.parentWatcher.teardown();\n    if (this.childWatcher) {\n      this.childWatcher.teardown();\n    }\n  }\n};\n\nvar queue$1 = [];\nvar queued = false;\n\n/**\n * Push a job into the queue.\n *\n * @param {Function} job\n */\n\nfunction pushJob(job) {\n  queue$1.push(job);\n  if (!queued) {\n    queued = true;\n    nextTick(flush);\n  }\n}\n\n/**\n * Flush the queue, and do one forced reflow before\n * triggering transitions.\n */\n\nfunction flush() {\n  // Force layout\n  var f = document.documentElement.offsetHeight;\n  for (var i = 0; i < queue$1.length; i++) {\n    queue$1[i]();\n  }\n  queue$1 = [];\n  queued = false;\n  // dummy return, so js linters don't complain about\n  // unused variable f\n  return f;\n}\n\nvar TYPE_TRANSITION = 'transition';\nvar TYPE_ANIMATION = 'animation';\nvar transDurationProp = transitionProp + 'Duration';\nvar animDurationProp = animationProp + 'Duration';\n\n/**\n * If a just-entered element is applied the\n * leave class while its enter transition hasn't started yet,\n * and the transitioned property has the same value for both\n * enter/leave, then the leave transition will be skipped and\n * the transitionend event never fires. This function ensures\n * its callback to be called after a transition has started\n * by waiting for double raf.\n *\n * It falls back to setTimeout on devices that support CSS\n * transitions but not raf (e.g. Android 4.2 browser) - since\n * these environments are usually slow, we are giving it a\n * relatively large timeout.\n */\n\nvar raf = inBrowser && window.requestAnimationFrame;\nvar waitForTransitionStart = raf\n/* istanbul ignore next */\n? function (fn) {\n  raf(function () {\n    raf(fn);\n  });\n} : function (fn) {\n  setTimeout(fn, 50);\n};\n\n/**\n * A Transition object that encapsulates the state and logic\n * of the transition.\n *\n * @param {Element} el\n * @param {String} id\n * @param {Object} hooks\n * @param {Vue} vm\n */\nfunction Transition(el, id, hooks, vm) {\n  this.id = id;\n  this.el = el;\n  this.enterClass = hooks && hooks.enterClass || id + '-enter';\n  this.leaveClass = hooks && hooks.leaveClass || id + '-leave';\n  this.hooks = hooks;\n  this.vm = vm;\n  // async state\n  this.pendingCssEvent = this.pendingCssCb = this.cancel = this.pendingJsCb = this.op = this.cb = null;\n  this.justEntered = false;\n  this.entered = this.left = false;\n  this.typeCache = {};\n  // check css transition type\n  this.type = hooks && hooks.type;\n  /* istanbul ignore if */\n  if ('production' !== 'production') {}\n  // bind\n  var self = this;['enterNextTick', 'enterDone', 'leaveNextTick', 'leaveDone'].forEach(function (m) {\n    self[m] = bind(self[m], self);\n  });\n}\n\nvar p$1 = Transition.prototype;\n\n/**\n * Start an entering transition.\n *\n * 1. enter transition triggered\n * 2. call beforeEnter hook\n * 3. add enter class\n * 4. insert/show element\n * 5. call enter hook (with possible explicit js callback)\n * 6. reflow\n * 7. based on transition type:\n *    - transition:\n *        remove class now, wait for transitionend,\n *        then done if there's no explicit js callback.\n *    - animation:\n *        wait for animationend, remove class,\n *        then done if there's no explicit js callback.\n *    - no css transition:\n *        done now if there's no explicit js callback.\n * 8. wait for either done or js callback, then call\n *    afterEnter hook.\n *\n * @param {Function} op - insert/show the element\n * @param {Function} [cb]\n */\n\np$1.enter = function (op, cb) {\n  this.cancelPending();\n  this.callHook('beforeEnter');\n  this.cb = cb;\n  addClass(this.el, this.enterClass);\n  op();\n  this.entered = false;\n  this.callHookWithCb('enter');\n  if (this.entered) {\n    return; // user called done synchronously.\n  }\n  this.cancel = this.hooks && this.hooks.enterCancelled;\n  pushJob(this.enterNextTick);\n};\n\n/**\n * The \"nextTick\" phase of an entering transition, which is\n * to be pushed into a queue and executed after a reflow so\n * that removing the class can trigger a CSS transition.\n */\n\np$1.enterNextTick = function () {\n  var _this = this;\n\n  // prevent transition skipping\n  this.justEntered = true;\n  waitForTransitionStart(function () {\n    _this.justEntered = false;\n  });\n  var enterDone = this.enterDone;\n  var type = this.getCssTransitionType(this.enterClass);\n  if (!this.pendingJsCb) {\n    if (type === TYPE_TRANSITION) {\n      // trigger transition by removing enter class now\n      removeClass(this.el, this.enterClass);\n      this.setupCssCb(transitionEndEvent, enterDone);\n    } else if (type === TYPE_ANIMATION) {\n      this.setupCssCb(animationEndEvent, enterDone);\n    } else {\n      enterDone();\n    }\n  } else if (type === TYPE_TRANSITION) {\n    removeClass(this.el, this.enterClass);\n  }\n};\n\n/**\n * The \"cleanup\" phase of an entering transition.\n */\n\np$1.enterDone = function () {\n  this.entered = true;\n  this.cancel = this.pendingJsCb = null;\n  removeClass(this.el, this.enterClass);\n  this.callHook('afterEnter');\n  if (this.cb) this.cb();\n};\n\n/**\n * Start a leaving transition.\n *\n * 1. leave transition triggered.\n * 2. call beforeLeave hook\n * 3. add leave class (trigger css transition)\n * 4. call leave hook (with possible explicit js callback)\n * 5. reflow if no explicit js callback is provided\n * 6. based on transition type:\n *    - transition or animation:\n *        wait for end event, remove class, then done if\n *        there's no explicit js callback.\n *    - no css transition:\n *        done if there's no explicit js callback.\n * 7. wait for either done or js callback, then call\n *    afterLeave hook.\n *\n * @param {Function} op - remove/hide the element\n * @param {Function} [cb]\n */\n\np$1.leave = function (op, cb) {\n  this.cancelPending();\n  this.callHook('beforeLeave');\n  this.op = op;\n  this.cb = cb;\n  addClass(this.el, this.leaveClass);\n  this.left = false;\n  this.callHookWithCb('leave');\n  if (this.left) {\n    return; // user called done synchronously.\n  }\n  this.cancel = this.hooks && this.hooks.leaveCancelled;\n  // only need to handle leaveDone if\n  // 1. the transition is already done (synchronously called\n  //    by the user, which causes this.op set to null)\n  // 2. there's no explicit js callback\n  if (this.op && !this.pendingJsCb) {\n    // if a CSS transition leaves immediately after enter,\n    // the transitionend event never fires. therefore we\n    // detect such cases and end the leave immediately.\n    if (this.justEntered) {\n      this.leaveDone();\n    } else {\n      pushJob(this.leaveNextTick);\n    }\n  }\n};\n\n/**\n * The \"nextTick\" phase of a leaving transition.\n */\n\np$1.leaveNextTick = function () {\n  var type = this.getCssTransitionType(this.leaveClass);\n  if (type) {\n    var event = type === TYPE_TRANSITION ? transitionEndEvent : animationEndEvent;\n    this.setupCssCb(event, this.leaveDone);\n  } else {\n    this.leaveDone();\n  }\n};\n\n/**\n * The \"cleanup\" phase of a leaving transition.\n */\n\np$1.leaveDone = function () {\n  this.left = true;\n  this.cancel = this.pendingJsCb = null;\n  this.op();\n  removeClass(this.el, this.leaveClass);\n  this.callHook('afterLeave');\n  if (this.cb) this.cb();\n  this.op = null;\n};\n\n/**\n * Cancel any pending callbacks from a previously running\n * but not finished transition.\n */\n\np$1.cancelPending = function () {\n  this.op = this.cb = null;\n  var hasPending = false;\n  if (this.pendingCssCb) {\n    hasPending = true;\n    off(this.el, this.pendingCssEvent, this.pendingCssCb);\n    this.pendingCssEvent = this.pendingCssCb = null;\n  }\n  if (this.pendingJsCb) {\n    hasPending = true;\n    this.pendingJsCb.cancel();\n    this.pendingJsCb = null;\n  }\n  if (hasPending) {\n    removeClass(this.el, this.enterClass);\n    removeClass(this.el, this.leaveClass);\n  }\n  if (this.cancel) {\n    this.cancel.call(this.vm, this.el);\n    this.cancel = null;\n  }\n};\n\n/**\n * Call a user-provided synchronous hook function.\n *\n * @param {String} type\n */\n\np$1.callHook = function (type) {\n  if (this.hooks && this.hooks[type]) {\n    this.hooks[type].call(this.vm, this.el);\n  }\n};\n\n/**\n * Call a user-provided, potentially-async hook function.\n * We check for the length of arguments to see if the hook\n * expects a `done` callback. If true, the transition's end\n * will be determined by when the user calls that callback;\n * otherwise, the end is determined by the CSS transition or\n * animation.\n *\n * @param {String} type\n */\n\np$1.callHookWithCb = function (type) {\n  var hook = this.hooks && this.hooks[type];\n  if (hook) {\n    if (hook.length > 1) {\n      this.pendingJsCb = cancellable(this[type + 'Done']);\n    }\n    hook.call(this.vm, this.el, this.pendingJsCb);\n  }\n};\n\n/**\n * Get an element's transition type based on the\n * calculated styles.\n *\n * @param {String} className\n * @return {Number}\n */\n\np$1.getCssTransitionType = function (className) {\n  /* istanbul ignore if */\n  if (!transitionEndEvent ||\n  // skip CSS transitions if page is not visible -\n  // this solves the issue of transitionend events not\n  // firing until the page is visible again.\n  // pageVisibility API is supported in IE10+, same as\n  // CSS transitions.\n  document.hidden ||\n  // explicit js-only transition\n  this.hooks && this.hooks.css === false ||\n  // element is hidden\n  isHidden(this.el)) {\n    return;\n  }\n  var type = this.type || this.typeCache[className];\n  if (type) return type;\n  var inlineStyles = this.el.style;\n  var computedStyles = window.getComputedStyle(this.el);\n  var transDuration = inlineStyles[transDurationProp] || computedStyles[transDurationProp];\n  if (transDuration && transDuration !== '0s') {\n    type = TYPE_TRANSITION;\n  } else {\n    var animDuration = inlineStyles[animDurationProp] || computedStyles[animDurationProp];\n    if (animDuration && animDuration !== '0s') {\n      type = TYPE_ANIMATION;\n    }\n  }\n  if (type) {\n    this.typeCache[className] = type;\n  }\n  return type;\n};\n\n/**\n * Setup a CSS transitionend/animationend callback.\n *\n * @param {String} event\n * @param {Function} cb\n */\n\np$1.setupCssCb = function (event, cb) {\n  this.pendingCssEvent = event;\n  var self = this;\n  var el = this.el;\n  var onEnd = this.pendingCssCb = function (e) {\n    if (e.target === el) {\n      off(el, event, onEnd);\n      self.pendingCssEvent = self.pendingCssCb = null;\n      if (!self.pendingJsCb && cb) {\n        cb();\n      }\n    }\n  };\n  on(el, event, onEnd);\n};\n\n/**\n * Check if an element is hidden - in that case we can just\n * skip the transition alltogether.\n *\n * @param {Element} el\n * @return {Boolean}\n */\n\nfunction isHidden(el) {\n  if (/svg$/.test(el.namespaceURI)) {\n    // SVG elements do not have offset(Width|Height)\n    // so we need to check the client rect\n    var rect = el.getBoundingClientRect();\n    return !(rect.width || rect.height);\n  } else {\n    return !(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n  }\n}\n\nvar transition$1 = {\n\n  priority: TRANSITION,\n\n  update: function update(id, oldId) {\n    var el = this.el;\n    // resolve on owner vm\n    var hooks = resolveAsset(this.vm.$options, 'transitions', id);\n    id = id || 'v';\n    oldId = oldId || 'v';\n    el.__v_trans = new Transition(el, id, hooks, this.vm);\n    removeClass(el, oldId + '-transition');\n    addClass(el, id + '-transition');\n  }\n};\n\nvar internalDirectives = {\n  style: style,\n  'class': vClass,\n  component: component,\n  prop: propDef,\n  transition: transition$1\n};\n\n// special binding prefixes\nvar bindRE = /^v-bind:|^:/;\nvar onRE = /^v-on:|^@/;\nvar dirAttrRE = /^v-([^:]+)(?:$|:(.*)$)/;\nvar modifierRE = /\\.[^\\.]+/g;\nvar transitionRE = /^(v-bind:|:)?transition$/;\n\n// default directive priority\nvar DEFAULT_PRIORITY = 1000;\nvar DEFAULT_TERMINAL_PRIORITY = 2000;\n\n/**\n * Compile a template and return a reusable composite link\n * function, which recursively contains more link functions\n * inside. This top level compile function would normally\n * be called on instance root nodes, but can also be used\n * for partial compilation if the partial argument is true.\n *\n * The returned composite link function, when called, will\n * return an unlink function that tearsdown all directives\n * created during the linking phase.\n *\n * @param {Element|DocumentFragment} el\n * @param {Object} options\n * @param {Boolean} partial\n * @return {Function}\n */\n\nfunction compile(el, options, partial) {\n  // link function for the node itself.\n  var nodeLinkFn = partial || !options._asComponent ? compileNode(el, options) : null;\n  // link function for the childNodes\n  var childLinkFn = !(nodeLinkFn && nodeLinkFn.terminal) && !isScript(el) && el.hasChildNodes() ? compileNodeList(el.childNodes, options) : null;\n\n  /**\n   * A composite linker function to be called on a already\n   * compiled piece of DOM, which instantiates all directive\n   * instances.\n   *\n   * @param {Vue} vm\n   * @param {Element|DocumentFragment} el\n   * @param {Vue} [host] - host vm of transcluded content\n   * @param {Object} [scope] - v-for scope\n   * @param {Fragment} [frag] - link context fragment\n   * @return {Function|undefined}\n   */\n\n  return function compositeLinkFn(vm, el, host, scope, frag) {\n    // cache childNodes before linking parent, fix #657\n    var childNodes = toArray(el.childNodes);\n    // link\n    var dirs = linkAndCapture(function compositeLinkCapturer() {\n      if (nodeLinkFn) nodeLinkFn(vm, el, host, scope, frag);\n      if (childLinkFn) childLinkFn(vm, childNodes, host, scope, frag);\n    }, vm);\n    return makeUnlinkFn(vm, dirs);\n  };\n}\n\n/**\n * Apply a linker to a vm/element pair and capture the\n * directives created during the process.\n *\n * @param {Function} linker\n * @param {Vue} vm\n */\n\nfunction linkAndCapture(linker, vm) {\n  /* istanbul ignore if */\n  if ('production' === 'production') {\n    // reset directives before every capture in production\n    // mode, so that when unlinking we don't need to splice\n    // them out (which turns out to be a perf hit).\n    // they are kept in development mode because they are\n    // useful for Vue's own tests.\n    vm._directives = [];\n  }\n  var originalDirCount = vm._directives.length;\n  linker();\n  var dirs = vm._directives.slice(originalDirCount);\n  sortDirectives(dirs);\n  for (var i = 0, l = dirs.length; i < l; i++) {\n    dirs[i]._bind();\n  }\n  return dirs;\n}\n\n/**\n * sort directives by priority (stable sort)\n *\n * @param {Array} dirs\n */\nfunction sortDirectives(dirs) {\n  if (dirs.length === 0) return;\n\n  var groupedMap = {};\n  var i, j, k, l;\n  var index = 0;\n  var priorities = [];\n  for (i = 0, j = dirs.length; i < j; i++) {\n    var dir = dirs[i];\n    var priority = dir.descriptor.def.priority || DEFAULT_PRIORITY;\n    var array = groupedMap[priority];\n    if (!array) {\n      array = groupedMap[priority] = [];\n      priorities.push(priority);\n    }\n    array.push(dir);\n  }\n\n  priorities.sort(function (a, b) {\n    return a > b ? -1 : a === b ? 0 : 1;\n  });\n  for (i = 0, j = priorities.length; i < j; i++) {\n    var group = groupedMap[priorities[i]];\n    for (k = 0, l = group.length; k < l; k++) {\n      dirs[index++] = group[k];\n    }\n  }\n}\n\n/**\n * Linker functions return an unlink function that\n * tearsdown all directives instances generated during\n * the process.\n *\n * We create unlink functions with only the necessary\n * information to avoid retaining additional closures.\n *\n * @param {Vue} vm\n * @param {Array} dirs\n * @param {Vue} [context]\n * @param {Array} [contextDirs]\n * @return {Function}\n */\n\nfunction makeUnlinkFn(vm, dirs, context, contextDirs) {\n  function unlink(destroying) {\n    teardownDirs(vm, dirs, destroying);\n    if (context && contextDirs) {\n      teardownDirs(context, contextDirs);\n    }\n  }\n  // expose linked directives\n  unlink.dirs = dirs;\n  return unlink;\n}\n\n/**\n * Teardown partial linked directives.\n *\n * @param {Vue} vm\n * @param {Array} dirs\n * @param {Boolean} destroying\n */\n\nfunction teardownDirs(vm, dirs, destroying) {\n  var i = dirs.length;\n  while (i--) {\n    dirs[i]._teardown();\n    if ('production' !== 'production' && !destroying) {}\n  }\n}\n\n/**\n * Compile link props on an instance.\n *\n * @param {Vue} vm\n * @param {Element} el\n * @param {Object} props\n * @param {Object} [scope]\n * @return {Function}\n */\n\nfunction compileAndLinkProps(vm, el, props, scope) {\n  var propsLinkFn = compileProps(el, props, vm);\n  var propDirs = linkAndCapture(function () {\n    propsLinkFn(vm, scope);\n  }, vm);\n  return makeUnlinkFn(vm, propDirs);\n}\n\n/**\n * Compile the root element of an instance.\n *\n * 1. attrs on context container (context scope)\n * 2. attrs on the component template root node, if\n *    replace:true (child scope)\n *\n * If this is a fragment instance, we only need to compile 1.\n *\n * @param {Element} el\n * @param {Object} options\n * @param {Object} contextOptions\n * @return {Function}\n */\n\nfunction compileRoot(el, options, contextOptions) {\n  var containerAttrs = options._containerAttrs;\n  var replacerAttrs = options._replacerAttrs;\n  var contextLinkFn, replacerLinkFn;\n\n  // only need to compile other attributes for\n  // non-fragment instances\n  if (el.nodeType !== 11) {\n    // for components, container and replacer need to be\n    // compiled separately and linked in different scopes.\n    if (options._asComponent) {\n      // 2. container attributes\n      if (containerAttrs && contextOptions) {\n        contextLinkFn = compileDirectives(containerAttrs, contextOptions);\n      }\n      if (replacerAttrs) {\n        // 3. replacer attributes\n        replacerLinkFn = compileDirectives(replacerAttrs, options);\n      }\n    } else {\n      // non-component, just compile as a normal element.\n      replacerLinkFn = compileDirectives(el.attributes, options);\n    }\n  } else if ('production' !== 'production' && containerAttrs) {}\n\n  options._containerAttrs = options._replacerAttrs = null;\n  return function rootLinkFn(vm, el, scope) {\n    // link context scope dirs\n    var context = vm._context;\n    var contextDirs;\n    if (context && contextLinkFn) {\n      contextDirs = linkAndCapture(function () {\n        contextLinkFn(context, el, null, scope);\n      }, context);\n    }\n\n    // link self\n    var selfDirs = linkAndCapture(function () {\n      if (replacerLinkFn) replacerLinkFn(vm, el);\n    }, vm);\n\n    // return the unlink function that tearsdown context\n    // container directives.\n    return makeUnlinkFn(vm, selfDirs, context, contextDirs);\n  };\n}\n\n/**\n * Compile a node and return a nodeLinkFn based on the\n * node type.\n *\n * @param {Node} node\n * @param {Object} options\n * @return {Function|null}\n */\n\nfunction compileNode(node, options) {\n  var type = node.nodeType;\n  if (type === 1 && !isScript(node)) {\n    return compileElement(node, options);\n  } else if (type === 3 && node.data.trim()) {\n    return compileTextNode(node, options);\n  } else {\n    return null;\n  }\n}\n\n/**\n * Compile an element and return a nodeLinkFn.\n *\n * @param {Element} el\n * @param {Object} options\n * @return {Function|null}\n */\n\nfunction compileElement(el, options) {\n  // preprocess textareas.\n  // textarea treats its text content as the initial value.\n  // just bind it as an attr directive for value.\n  if (el.tagName === 'TEXTAREA') {\n    // a textarea which has v-pre attr should skip complie.\n    if (getAttr(el, 'v-pre') !== null) {\n      return skip;\n    }\n    var tokens = parseText(el.value);\n    if (tokens) {\n      el.setAttribute(':value', tokensToExp(tokens));\n      el.value = '';\n    }\n  }\n  var linkFn;\n  var hasAttrs = el.hasAttributes();\n  var attrs = hasAttrs && toArray(el.attributes);\n  // check terminal directives (for & if)\n  if (hasAttrs) {\n    linkFn = checkTerminalDirectives(el, attrs, options);\n  }\n  // check element directives\n  if (!linkFn) {\n    linkFn = checkElementDirectives(el, options);\n  }\n  // check component\n  if (!linkFn) {\n    linkFn = checkComponent(el, options);\n  }\n  // normal directives\n  if (!linkFn && hasAttrs) {\n    linkFn = compileDirectives(attrs, options);\n  }\n  return linkFn;\n}\n\n/**\n * Compile a textNode and return a nodeLinkFn.\n *\n * @param {TextNode} node\n * @param {Object} options\n * @return {Function|null} textNodeLinkFn\n */\n\nfunction compileTextNode(node, options) {\n  // skip marked text nodes\n  if (node._skip) {\n    return removeText;\n  }\n\n  var tokens = parseText(node.wholeText);\n  if (!tokens) {\n    return null;\n  }\n\n  // mark adjacent text nodes as skipped,\n  // because we are using node.wholeText to compile\n  // all adjacent text nodes together. This fixes\n  // issues in IE where sometimes it splits up a single\n  // text node into multiple ones.\n  var next = node.nextSibling;\n  while (next && next.nodeType === 3) {\n    next._skip = true;\n    next = next.nextSibling;\n  }\n\n  var frag = document.createDocumentFragment();\n  var el, token;\n  for (var i = 0, l = tokens.length; i < l; i++) {\n    token = tokens[i];\n    el = token.tag ? processTextToken(token, options) : document.createTextNode(token.value);\n    frag.appendChild(el);\n  }\n  return makeTextNodeLinkFn(tokens, frag, options);\n}\n\n/**\n * Linker for an skipped text node.\n *\n * @param {Vue} vm\n * @param {Text} node\n */\n\nfunction removeText(vm, node) {\n  remove(node);\n}\n\n/**\n * Process a single text token.\n *\n * @param {Object} token\n * @param {Object} options\n * @return {Node}\n */\n\nfunction processTextToken(token, options) {\n  var el;\n  if (token.oneTime) {\n    el = document.createTextNode(token.value);\n  } else {\n    if (token.html) {\n      el = document.createComment('v-html');\n      setTokenType('html');\n    } else {\n      // IE will clean up empty textNodes during\n      // frag.cloneNode(true), so we have to give it\n      // something here...\n      el = document.createTextNode(' ');\n      setTokenType('text');\n    }\n  }\n  function setTokenType(type) {\n    if (token.descriptor) return;\n    var parsed = parseDirective(token.value);\n    token.descriptor = {\n      name: type,\n      def: directives[type],\n      expression: parsed.expression,\n      filters: parsed.filters\n    };\n  }\n  return el;\n}\n\n/**\n * Build a function that processes a textNode.\n *\n * @param {Array<Object>} tokens\n * @param {DocumentFragment} frag\n */\n\nfunction makeTextNodeLinkFn(tokens, frag) {\n  return function textNodeLinkFn(vm, el, host, scope) {\n    var fragClone = frag.cloneNode(true);\n    var childNodes = toArray(fragClone.childNodes);\n    var token, value, node;\n    for (var i = 0, l = tokens.length; i < l; i++) {\n      token = tokens[i];\n      value = token.value;\n      if (token.tag) {\n        node = childNodes[i];\n        if (token.oneTime) {\n          value = (scope || vm).$eval(value);\n          if (token.html) {\n            replace(node, parseTemplate(value, true));\n          } else {\n            node.data = _toString(value);\n          }\n        } else {\n          vm._bindDir(token.descriptor, node, host, scope);\n        }\n      }\n    }\n    replace(el, fragClone);\n  };\n}\n\n/**\n * Compile a node list and return a childLinkFn.\n *\n * @param {NodeList} nodeList\n * @param {Object} options\n * @return {Function|undefined}\n */\n\nfunction compileNodeList(nodeList, options) {\n  var linkFns = [];\n  var nodeLinkFn, childLinkFn, node;\n  for (var i = 0, l = nodeList.length; i < l; i++) {\n    node = nodeList[i];\n    nodeLinkFn = compileNode(node, options);\n    childLinkFn = !(nodeLinkFn && nodeLinkFn.terminal) && node.tagName !== 'SCRIPT' && node.hasChildNodes() ? compileNodeList(node.childNodes, options) : null;\n    linkFns.push(nodeLinkFn, childLinkFn);\n  }\n  return linkFns.length ? makeChildLinkFn(linkFns) : null;\n}\n\n/**\n * Make a child link function for a node's childNodes.\n *\n * @param {Array<Function>} linkFns\n * @return {Function} childLinkFn\n */\n\nfunction makeChildLinkFn(linkFns) {\n  return function childLinkFn(vm, nodes, host, scope, frag) {\n    var node, nodeLinkFn, childrenLinkFn;\n    for (var i = 0, n = 0, l = linkFns.length; i < l; n++) {\n      node = nodes[n];\n      nodeLinkFn = linkFns[i++];\n      childrenLinkFn = linkFns[i++];\n      // cache childNodes before linking parent, fix #657\n      var childNodes = toArray(node.childNodes);\n      if (nodeLinkFn) {\n        nodeLinkFn(vm, node, host, scope, frag);\n      }\n      if (childrenLinkFn) {\n        childrenLinkFn(vm, childNodes, host, scope, frag);\n      }\n    }\n  };\n}\n\n/**\n * Check for element directives (custom elements that should\n * be resovled as terminal directives).\n *\n * @param {Element} el\n * @param {Object} options\n */\n\nfunction checkElementDirectives(el, options) {\n  var tag = el.tagName.toLowerCase();\n  if (commonTagRE.test(tag)) {\n    return;\n  }\n  var def = resolveAsset(options, 'elementDirectives', tag);\n  if (def) {\n    return makeTerminalNodeLinkFn(el, tag, '', options, def);\n  }\n}\n\n/**\n * Check if an element is a component. If yes, return\n * a component link function.\n *\n * @param {Element} el\n * @param {Object} options\n * @return {Function|undefined}\n */\n\nfunction checkComponent(el, options) {\n  var component = checkComponentAttr(el, options);\n  if (component) {\n    var ref = findRef(el);\n    var descriptor = {\n      name: 'component',\n      ref: ref,\n      expression: component.id,\n      def: internalDirectives.component,\n      modifiers: {\n        literal: !component.dynamic\n      }\n    };\n    var componentLinkFn = function componentLinkFn(vm, el, host, scope, frag) {\n      if (ref) {\n        defineReactive((scope || vm).$refs, ref, null);\n      }\n      vm._bindDir(descriptor, el, host, scope, frag);\n    };\n    componentLinkFn.terminal = true;\n    return componentLinkFn;\n  }\n}\n\n/**\n * Check an element for terminal directives in fixed order.\n * If it finds one, return a terminal link function.\n *\n * @param {Element} el\n * @param {Array} attrs\n * @param {Object} options\n * @return {Function} terminalLinkFn\n */\n\nfunction checkTerminalDirectives(el, attrs, options) {\n  // skip v-pre\n  if (getAttr(el, 'v-pre') !== null) {\n    return skip;\n  }\n  // skip v-else block, but only if following v-if\n  if (el.hasAttribute('v-else')) {\n    var prev = el.previousElementSibling;\n    if (prev && prev.hasAttribute('v-if')) {\n      return skip;\n    }\n  }\n\n  var attr, name, value, modifiers, matched, dirName, rawName, arg, def, termDef;\n  for (var i = 0, j = attrs.length; i < j; i++) {\n    attr = attrs[i];\n    name = attr.name.replace(modifierRE, '');\n    if (matched = name.match(dirAttrRE)) {\n      def = resolveAsset(options, 'directives', matched[1]);\n      if (def && def.terminal) {\n        if (!termDef || (def.priority || DEFAULT_TERMINAL_PRIORITY) > termDef.priority) {\n          termDef = def;\n          rawName = attr.name;\n          modifiers = parseModifiers(attr.name);\n          value = attr.value;\n          dirName = matched[1];\n          arg = matched[2];\n        }\n      }\n    }\n  }\n\n  if (termDef) {\n    return makeTerminalNodeLinkFn(el, dirName, value, options, termDef, rawName, arg, modifiers);\n  }\n}\n\nfunction skip() {}\nskip.terminal = true;\n\n/**\n * Build a node link function for a terminal directive.\n * A terminal link function terminates the current\n * compilation recursion and handles compilation of the\n * subtree in the directive.\n *\n * @param {Element} el\n * @param {String} dirName\n * @param {String} value\n * @param {Object} options\n * @param {Object} def\n * @param {String} [rawName]\n * @param {String} [arg]\n * @param {Object} [modifiers]\n * @return {Function} terminalLinkFn\n */\n\nfunction makeTerminalNodeLinkFn(el, dirName, value, options, def, rawName, arg, modifiers) {\n  var parsed = parseDirective(value);\n  var descriptor = {\n    name: dirName,\n    arg: arg,\n    expression: parsed.expression,\n    filters: parsed.filters,\n    raw: value,\n    attr: rawName,\n    modifiers: modifiers,\n    def: def\n  };\n  // check ref for v-for, v-if and router-view\n  if (dirName === 'for' || dirName === 'router-view') {\n    descriptor.ref = findRef(el);\n  }\n  var fn = function terminalNodeLinkFn(vm, el, host, scope, frag) {\n    if (descriptor.ref) {\n      defineReactive((scope || vm).$refs, descriptor.ref, null);\n    }\n    vm._bindDir(descriptor, el, host, scope, frag);\n  };\n  fn.terminal = true;\n  return fn;\n}\n\n/**\n * Compile the directives on an element and return a linker.\n *\n * @param {Array|NamedNodeMap} attrs\n * @param {Object} options\n * @return {Function}\n */\n\nfunction compileDirectives(attrs, options) {\n  var i = attrs.length;\n  var dirs = [];\n  var attr, name, value, rawName, rawValue, dirName, arg, modifiers, dirDef, tokens, matched;\n  while (i--) {\n    attr = attrs[i];\n    name = rawName = attr.name;\n    value = rawValue = attr.value;\n    tokens = parseText(value);\n    // reset arg\n    arg = null;\n    // check modifiers\n    modifiers = parseModifiers(name);\n    name = name.replace(modifierRE, '');\n\n    // attribute interpolations\n    if (tokens) {\n      value = tokensToExp(tokens);\n      arg = name;\n      pushDir('bind', directives.bind, tokens);\n      // warn against mixing mustaches with v-bind\n      if ('production' !== 'production') {}\n    } else\n\n      // special attribute: transition\n      if (transitionRE.test(name)) {\n        modifiers.literal = !bindRE.test(name);\n        pushDir('transition', internalDirectives.transition);\n      } else\n\n        // event handlers\n        if (onRE.test(name)) {\n          arg = name.replace(onRE, '');\n          pushDir('on', directives.on);\n        } else\n\n          // attribute bindings\n          if (bindRE.test(name)) {\n            dirName = name.replace(bindRE, '');\n            if (dirName === 'style' || dirName === 'class') {\n              pushDir(dirName, internalDirectives[dirName]);\n            } else {\n              arg = dirName;\n              pushDir('bind', directives.bind);\n            }\n          } else\n\n            // normal directives\n            if (matched = name.match(dirAttrRE)) {\n              dirName = matched[1];\n              arg = matched[2];\n\n              // skip v-else (when used with v-show)\n              if (dirName === 'else') {\n                continue;\n              }\n\n              dirDef = resolveAsset(options, 'directives', dirName, true);\n              if (dirDef) {\n                pushDir(dirName, dirDef);\n              }\n            }\n  }\n\n  /**\n   * Push a directive.\n   *\n   * @param {String} dirName\n   * @param {Object|Function} def\n   * @param {Array} [interpTokens]\n   */\n\n  function pushDir(dirName, def, interpTokens) {\n    var hasOneTimeToken = interpTokens && hasOneTime(interpTokens);\n    var parsed = !hasOneTimeToken && parseDirective(value);\n    dirs.push({\n      name: dirName,\n      attr: rawName,\n      raw: rawValue,\n      def: def,\n      arg: arg,\n      modifiers: modifiers,\n      // conversion from interpolation strings with one-time token\n      // to expression is differed until directive bind time so that we\n      // have access to the actual vm context for one-time bindings.\n      expression: parsed && parsed.expression,\n      filters: parsed && parsed.filters,\n      interp: interpTokens,\n      hasOneTime: hasOneTimeToken\n    });\n  }\n\n  if (dirs.length) {\n    return makeNodeLinkFn(dirs);\n  }\n}\n\n/**\n * Parse modifiers from directive attribute name.\n *\n * @param {String} name\n * @return {Object}\n */\n\nfunction parseModifiers(name) {\n  var res = Object.create(null);\n  var match = name.match(modifierRE);\n  if (match) {\n    var i = match.length;\n    while (i--) {\n      res[match[i].slice(1)] = true;\n    }\n  }\n  return res;\n}\n\n/**\n * Build a link function for all directives on a single node.\n *\n * @param {Array} directives\n * @return {Function} directivesLinkFn\n */\n\nfunction makeNodeLinkFn(directives) {\n  return function nodeLinkFn(vm, el, host, scope, frag) {\n    // reverse apply because it's sorted low to high\n    var i = directives.length;\n    while (i--) {\n      vm._bindDir(directives[i], el, host, scope, frag);\n    }\n  };\n}\n\n/**\n * Check if an interpolation string contains one-time tokens.\n *\n * @param {Array} tokens\n * @return {Boolean}\n */\n\nfunction hasOneTime(tokens) {\n  var i = tokens.length;\n  while (i--) {\n    if (tokens[i].oneTime) return true;\n  }\n}\n\nfunction isScript(el) {\n  return el.tagName === 'SCRIPT' && (!el.hasAttribute('type') || el.getAttribute('type') === 'text/javascript');\n}\n\nvar specialCharRE = /[^\\w\\-:\\.]/;\n\n/**\n * Process an element or a DocumentFragment based on a\n * instance option object. This allows us to transclude\n * a template node/fragment before the instance is created,\n * so the processed fragment can then be cloned and reused\n * in v-for.\n *\n * @param {Element} el\n * @param {Object} options\n * @return {Element|DocumentFragment}\n */\n\nfunction transclude(el, options) {\n  // extract container attributes to pass them down\n  // to compiler, because they need to be compiled in\n  // parent scope. we are mutating the options object here\n  // assuming the same object will be used for compile\n  // right after this.\n  if (options) {\n    options._containerAttrs = extractAttrs(el);\n  }\n  // for template tags, what we want is its content as\n  // a documentFragment (for fragment instances)\n  if (isTemplate(el)) {\n    el = parseTemplate(el);\n  }\n  if (options) {\n    if (options._asComponent && !options.template) {\n      options.template = '<slot></slot>';\n    }\n    if (options.template) {\n      options._content = extractContent(el);\n      el = transcludeTemplate(el, options);\n    }\n  }\n  if (isFragment(el)) {\n    // anchors for fragment instance\n    // passing in `persist: true` to avoid them being\n    // discarded by IE during template cloning\n    prepend(createAnchor('v-start', true), el);\n    el.appendChild(createAnchor('v-end', true));\n  }\n  return el;\n}\n\n/**\n * Process the template option.\n * If the replace option is true this will swap the $el.\n *\n * @param {Element} el\n * @param {Object} options\n * @return {Element|DocumentFragment}\n */\n\nfunction transcludeTemplate(el, options) {\n  var template = options.template;\n  var frag = parseTemplate(template, true);\n  if (frag) {\n    var replacer = frag.firstChild;\n    if (!replacer) {\n      return frag;\n    }\n    var tag = replacer.tagName && replacer.tagName.toLowerCase();\n    if (options.replace) {\n      /* istanbul ignore if */\n      if (el === document.body) {\n        'production' !== 'production' && warn('You are mounting an instance with a template to ' + '<body>. This will replace <body> entirely. You ' + 'should probably use `replace: false` here.');\n      }\n      // there are many cases where the instance must\n      // become a fragment instance: basically anything that\n      // can create more than 1 root nodes.\n      if (\n      // multi-children template\n      frag.childNodes.length > 1 ||\n      // non-element template\n      replacer.nodeType !== 1 ||\n      // single nested component\n      tag === 'component' || resolveAsset(options, 'components', tag) || hasBindAttr(replacer, 'is') ||\n      // element directive\n      resolveAsset(options, 'elementDirectives', tag) ||\n      // for block\n      replacer.hasAttribute('v-for') ||\n      // if block\n      replacer.hasAttribute('v-if')) {\n        return frag;\n      } else {\n        options._replacerAttrs = extractAttrs(replacer);\n        mergeAttrs(el, replacer);\n        return replacer;\n      }\n    } else {\n      el.appendChild(frag);\n      return el;\n    }\n  } else {\n    'production' !== 'production' && warn('Invalid template option: ' + template);\n  }\n}\n\n/**\n * Helper to extract a component container's attributes\n * into a plain object array.\n *\n * @param {Element} el\n * @return {Array}\n */\n\nfunction extractAttrs(el) {\n  if (el.nodeType === 1 && el.hasAttributes()) {\n    return toArray(el.attributes);\n  }\n}\n\n/**\n * Merge the attributes of two elements, and make sure\n * the class names are merged properly.\n *\n * @param {Element} from\n * @param {Element} to\n */\n\nfunction mergeAttrs(from, to) {\n  var attrs = from.attributes;\n  var i = attrs.length;\n  var name, value;\n  while (i--) {\n    name = attrs[i].name;\n    value = attrs[i].value;\n    if (!to.hasAttribute(name) && !specialCharRE.test(name)) {\n      to.setAttribute(name, value);\n    } else if (name === 'class' && !parseText(value) && (value = value.trim())) {\n      value.split(/\\s+/).forEach(function (cls) {\n        addClass(to, cls);\n      });\n    }\n  }\n}\n\n/**\n * Scan and determine slot content distribution.\n * We do this during transclusion instead at compile time so that\n * the distribution is decoupled from the compilation order of\n * the slots.\n *\n * @param {Element|DocumentFragment} template\n * @param {Element} content\n * @param {Vue} vm\n */\n\nfunction resolveSlots(vm, content) {\n  if (!content) {\n    return;\n  }\n  var contents = vm._slotContents = Object.create(null);\n  var el, name;\n  for (var i = 0, l = content.children.length; i < l; i++) {\n    el = content.children[i];\n    /* eslint-disable no-cond-assign */\n    if (name = el.getAttribute('slot')) {\n      (contents[name] || (contents[name] = [])).push(el);\n    }\n    /* eslint-enable no-cond-assign */\n    if ('production' !== 'production' && getBindAttr(el, 'slot')) {}\n  }\n  for (name in contents) {\n    contents[name] = extractFragment(contents[name], content);\n  }\n  if (content.hasChildNodes()) {\n    var nodes = content.childNodes;\n    if (nodes.length === 1 && nodes[0].nodeType === 3 && !nodes[0].data.trim()) {\n      return;\n    }\n    contents['default'] = extractFragment(content.childNodes, content);\n  }\n}\n\n/**\n * Extract qualified content nodes from a node list.\n *\n * @param {NodeList} nodes\n * @return {DocumentFragment}\n */\n\nfunction extractFragment(nodes, parent) {\n  var frag = document.createDocumentFragment();\n  nodes = toArray(nodes);\n  for (var i = 0, l = nodes.length; i < l; i++) {\n    var node = nodes[i];\n    if (isTemplate(node) && !node.hasAttribute('v-if') && !node.hasAttribute('v-for')) {\n      parent.removeChild(node);\n      node = parseTemplate(node, true);\n    }\n    frag.appendChild(node);\n  }\n  return frag;\n}\n\n\n\nvar compiler = Object.freeze({\n\tcompile: compile,\n\tcompileAndLinkProps: compileAndLinkProps,\n\tcompileRoot: compileRoot,\n\ttransclude: transclude,\n\tresolveSlots: resolveSlots\n});\n\nfunction stateMixin (Vue) {\n  /**\n   * Accessor for `$data` property, since setting $data\n   * requires observing the new object and updating\n   * proxied properties.\n   */\n\n  Object.defineProperty(Vue.prototype, '$data', {\n    get: function get() {\n      return this._data;\n    },\n    set: function set(newData) {\n      if (newData !== this._data) {\n        this._setData(newData);\n      }\n    }\n  });\n\n  /**\n   * Setup the scope of an instance, which contains:\n   * - observed data\n   * - computed properties\n   * - user methods\n   * - meta properties\n   */\n\n  Vue.prototype._initState = function () {\n    this._initProps();\n    this._initMeta();\n    this._initMethods();\n    this._initData();\n    this._initComputed();\n  };\n\n  /**\n   * Initialize props.\n   */\n\n  Vue.prototype._initProps = function () {\n    var options = this.$options;\n    var el = options.el;\n    var props = options.props;\n    if (props && !el) {\n      'production' !== 'production' && warn('Props will not be compiled if no `el` option is ' + 'provided at instantiation.', this);\n    }\n    // make sure to convert string selectors into element now\n    el = options.el = query(el);\n    this._propsUnlinkFn = el && el.nodeType === 1 && props\n    // props must be linked in proper scope if inside v-for\n    ? compileAndLinkProps(this, el, props, this._scope) : null;\n  };\n\n  /**\n   * Initialize the data.\n   */\n\n  Vue.prototype._initData = function () {\n    var dataFn = this.$options.data;\n    var data = this._data = dataFn ? dataFn() : {};\n    if (!isPlainObject(data)) {\n      data = {};\n      'production' !== 'production' && warn('data functions should return an object.', this);\n    }\n    var props = this._props;\n    // proxy data on instance\n    var keys = Object.keys(data);\n    var i, key;\n    i = keys.length;\n    while (i--) {\n      key = keys[i];\n      // there are two scenarios where we can proxy a data key:\n      // 1. it's not already defined as a prop\n      // 2. it's provided via a instantiation option AND there are no\n      //    template prop present\n      if (!props || !hasOwn(props, key)) {\n        this._proxy(key);\n      } else if ('production' !== 'production') {}\n    }\n    // observe data\n    observe(data, this);\n  };\n\n  /**\n   * Swap the instance's $data. Called in $data's setter.\n   *\n   * @param {Object} newData\n   */\n\n  Vue.prototype._setData = function (newData) {\n    newData = newData || {};\n    var oldData = this._data;\n    this._data = newData;\n    var keys, key, i;\n    // unproxy keys not present in new data\n    keys = Object.keys(oldData);\n    i = keys.length;\n    while (i--) {\n      key = keys[i];\n      if (!(key in newData)) {\n        this._unproxy(key);\n      }\n    }\n    // proxy keys not already proxied,\n    // and trigger change for changed values\n    keys = Object.keys(newData);\n    i = keys.length;\n    while (i--) {\n      key = keys[i];\n      if (!hasOwn(this, key)) {\n        // new property\n        this._proxy(key);\n      }\n    }\n    oldData.__ob__.removeVm(this);\n    observe(newData, this);\n    this._digest();\n  };\n\n  /**\n   * Proxy a property, so that\n   * vm.prop === vm._data.prop\n   *\n   * @param {String} key\n   */\n\n  Vue.prototype._proxy = function (key) {\n    if (!isReserved(key)) {\n      // need to store ref to self here\n      // because these getter/setters might\n      // be called by child scopes via\n      // prototype inheritance.\n      var self = this;\n      Object.defineProperty(self, key, {\n        configurable: true,\n        enumerable: true,\n        get: function proxyGetter() {\n          return self._data[key];\n        },\n        set: function proxySetter(val) {\n          self._data[key] = val;\n        }\n      });\n    }\n  };\n\n  /**\n   * Unproxy a property.\n   *\n   * @param {String} key\n   */\n\n  Vue.prototype._unproxy = function (key) {\n    if (!isReserved(key)) {\n      delete this[key];\n    }\n  };\n\n  /**\n   * Force update on every watcher in scope.\n   */\n\n  Vue.prototype._digest = function () {\n    for (var i = 0, l = this._watchers.length; i < l; i++) {\n      this._watchers[i].update(true); // shallow updates\n    }\n  };\n\n  /**\n   * Setup computed properties. They are essentially\n   * special getter/setters\n   */\n\n  function noop() {}\n  Vue.prototype._initComputed = function () {\n    var computed = this.$options.computed;\n    if (computed) {\n      for (var key in computed) {\n        var userDef = computed[key];\n        var def = {\n          enumerable: true,\n          configurable: true\n        };\n        if (typeof userDef === 'function') {\n          def.get = makeComputedGetter(userDef, this);\n          def.set = noop;\n        } else {\n          def.get = userDef.get ? userDef.cache !== false ? makeComputedGetter(userDef.get, this) : bind(userDef.get, this) : noop;\n          def.set = userDef.set ? bind(userDef.set, this) : noop;\n        }\n        Object.defineProperty(this, key, def);\n      }\n    }\n  };\n\n  function makeComputedGetter(getter, owner) {\n    var watcher = new Watcher(owner, getter, null, {\n      lazy: true\n    });\n    return function computedGetter() {\n      if (watcher.dirty) {\n        watcher.evaluate();\n      }\n      if (Dep.target) {\n        watcher.depend();\n      }\n      return watcher.value;\n    };\n  }\n\n  /**\n   * Setup instance methods. Methods must be bound to the\n   * instance since they might be passed down as a prop to\n   * child components.\n   */\n\n  Vue.prototype._initMethods = function () {\n    var methods = this.$options.methods;\n    if (methods) {\n      for (var key in methods) {\n        this[key] = bind(methods[key], this);\n      }\n    }\n  };\n\n  /**\n   * Initialize meta information like $index, $key & $value.\n   */\n\n  Vue.prototype._initMeta = function () {\n    var metas = this.$options._meta;\n    if (metas) {\n      for (var key in metas) {\n        defineReactive(this, key, metas[key]);\n      }\n    }\n  };\n}\n\nvar eventRE = /^v-on:|^@/;\n\nfunction eventsMixin (Vue) {\n  /**\n   * Setup the instance's option events & watchers.\n   * If the value is a string, we pull it from the\n   * instance's methods by name.\n   */\n\n  Vue.prototype._initEvents = function () {\n    var options = this.$options;\n    if (options._asComponent) {\n      registerComponentEvents(this, options.el);\n    }\n    registerCallbacks(this, '$on', options.events);\n    registerCallbacks(this, '$watch', options.watch);\n  };\n\n  /**\n   * Register v-on events on a child component\n   *\n   * @param {Vue} vm\n   * @param {Element} el\n   */\n\n  function registerComponentEvents(vm, el) {\n    var attrs = el.attributes;\n    var name, value, handler;\n    for (var i = 0, l = attrs.length; i < l; i++) {\n      name = attrs[i].name;\n      if (eventRE.test(name)) {\n        name = name.replace(eventRE, '');\n        // force the expression into a statement so that\n        // it always dynamically resolves the method to call (#2670)\n        // kinda ugly hack, but does the job.\n        value = attrs[i].value;\n        if (isSimplePath(value)) {\n          value += '.apply(this, $arguments)';\n        }\n        handler = (vm._scope || vm._context).$eval(value, true);\n        handler._fromParent = true;\n        vm.$on(name.replace(eventRE), handler);\n      }\n    }\n  }\n\n  /**\n   * Register callbacks for option events and watchers.\n   *\n   * @param {Vue} vm\n   * @param {String} action\n   * @param {Object} hash\n   */\n\n  function registerCallbacks(vm, action, hash) {\n    if (!hash) return;\n    var handlers, key, i, j;\n    for (key in hash) {\n      handlers = hash[key];\n      if (isArray(handlers)) {\n        for (i = 0, j = handlers.length; i < j; i++) {\n          register(vm, action, key, handlers[i]);\n        }\n      } else {\n        register(vm, action, key, handlers);\n      }\n    }\n  }\n\n  /**\n   * Helper to register an event/watch callback.\n   *\n   * @param {Vue} vm\n   * @param {String} action\n   * @param {String} key\n   * @param {Function|String|Object} handler\n   * @param {Object} [options]\n   */\n\n  function register(vm, action, key, handler, options) {\n    var type = typeof handler;\n    if (type === 'function') {\n      vm[action](key, handler, options);\n    } else if (type === 'string') {\n      var methods = vm.$options.methods;\n      var method = methods && methods[handler];\n      if (method) {\n        vm[action](key, method, options);\n      } else {\n        'production' !== 'production' && warn('Unknown method: \"' + handler + '\" when ' + 'registering callback for ' + action + ': \"' + key + '\".', vm);\n      }\n    } else if (handler && type === 'object') {\n      register(vm, action, key, handler.handler, handler);\n    }\n  }\n\n  /**\n   * Setup recursive attached/detached calls\n   */\n\n  Vue.prototype._initDOMHooks = function () {\n    this.$on('hook:attached', onAttached);\n    this.$on('hook:detached', onDetached);\n  };\n\n  /**\n   * Callback to recursively call attached hook on children\n   */\n\n  function onAttached() {\n    if (!this._isAttached) {\n      this._isAttached = true;\n      this.$children.forEach(callAttach);\n    }\n  }\n\n  /**\n   * Iterator to call attached hook\n   *\n   * @param {Vue} child\n   */\n\n  function callAttach(child) {\n    if (!child._isAttached && inDoc(child.$el)) {\n      child._callHook('attached');\n    }\n  }\n\n  /**\n   * Callback to recursively call detached hook on children\n   */\n\n  function onDetached() {\n    if (this._isAttached) {\n      this._isAttached = false;\n      this.$children.forEach(callDetach);\n    }\n  }\n\n  /**\n   * Iterator to call detached hook\n   *\n   * @param {Vue} child\n   */\n\n  function callDetach(child) {\n    if (child._isAttached && !inDoc(child.$el)) {\n      child._callHook('detached');\n    }\n  }\n\n  /**\n   * Trigger all handlers for a hook\n   *\n   * @param {String} hook\n   */\n\n  Vue.prototype._callHook = function (hook) {\n    this.$emit('pre-hook:' + hook);\n    var handlers = this.$options[hook];\n    if (handlers) {\n      for (var i = 0, j = handlers.length; i < j; i++) {\n        handlers[i].call(this);\n      }\n    }\n    this.$emit('hook:' + hook);\n  };\n}\n\nfunction noop$1() {}\n\n/**\n * A directive links a DOM element with a piece of data,\n * which is the result of evaluating an expression.\n * It registers a watcher with the expression and calls\n * the DOM update function when a change is triggered.\n *\n * @param {Object} descriptor\n *                 - {String} name\n *                 - {Object} def\n *                 - {String} expression\n *                 - {Array<Object>} [filters]\n *                 - {Object} [modifiers]\n *                 - {Boolean} literal\n *                 - {String} attr\n *                 - {String} arg\n *                 - {String} raw\n *                 - {String} [ref]\n *                 - {Array<Object>} [interp]\n *                 - {Boolean} [hasOneTime]\n * @param {Vue} vm\n * @param {Node} el\n * @param {Vue} [host] - transclusion host component\n * @param {Object} [scope] - v-for scope\n * @param {Fragment} [frag] - owner fragment\n * @constructor\n */\nfunction Directive(descriptor, vm, el, host, scope, frag) {\n  this.vm = vm;\n  this.el = el;\n  // copy descriptor properties\n  this.descriptor = descriptor;\n  this.name = descriptor.name;\n  this.expression = descriptor.expression;\n  this.arg = descriptor.arg;\n  this.modifiers = descriptor.modifiers;\n  this.filters = descriptor.filters;\n  this.literal = this.modifiers && this.modifiers.literal;\n  // private\n  this._locked = false;\n  this._bound = false;\n  this._listeners = null;\n  // link context\n  this._host = host;\n  this._scope = scope;\n  this._frag = frag;\n  // store directives on node in dev mode\n  if ('production' !== 'production' && this.el) {}\n}\n\n/**\n * Initialize the directive, mixin definition properties,\n * setup the watcher, call definition bind() and update()\n * if present.\n */\n\nDirective.prototype._bind = function () {\n  var name = this.name;\n  var descriptor = this.descriptor;\n\n  // remove attribute\n  if ((name !== 'cloak' || this.vm._isCompiled) && this.el && this.el.removeAttribute) {\n    var attr = descriptor.attr || 'v-' + name;\n    this.el.removeAttribute(attr);\n  }\n\n  // copy def properties\n  var def = descriptor.def;\n  if (typeof def === 'function') {\n    this.update = def;\n  } else {\n    extend(this, def);\n  }\n\n  // setup directive params\n  this._setupParams();\n\n  // initial bind\n  if (this.bind) {\n    this.bind();\n  }\n  this._bound = true;\n\n  if (this.literal) {\n    this.update && this.update(descriptor.raw);\n  } else if ((this.expression || this.modifiers) && (this.update || this.twoWay) && !this._checkStatement()) {\n    // wrapped updater for context\n    var dir = this;\n    if (this.update) {\n      this._update = function (val, oldVal) {\n        if (!dir._locked) {\n          dir.update(val, oldVal);\n        }\n      };\n    } else {\n      this._update = noop$1;\n    }\n    var preProcess = this._preProcess ? bind(this._preProcess, this) : null;\n    var postProcess = this._postProcess ? bind(this._postProcess, this) : null;\n    var watcher = this._watcher = new Watcher(this.vm, this.expression, this._update, // callback\n    {\n      filters: this.filters,\n      twoWay: this.twoWay,\n      deep: this.deep,\n      preProcess: preProcess,\n      postProcess: postProcess,\n      scope: this._scope\n    });\n    // v-model with inital inline value need to sync back to\n    // model instead of update to DOM on init. They would\n    // set the afterBind hook to indicate that.\n    if (this.afterBind) {\n      this.afterBind();\n    } else if (this.update) {\n      this.update(watcher.value);\n    }\n  }\n};\n\n/**\n * Setup all param attributes, e.g. track-by,\n * transition-mode, etc...\n */\n\nDirective.prototype._setupParams = function () {\n  if (!this.params) {\n    return;\n  }\n  var params = this.params;\n  // swap the params array with a fresh object.\n  this.params = Object.create(null);\n  var i = params.length;\n  var key, val, mappedKey;\n  while (i--) {\n    key = hyphenate(params[i]);\n    mappedKey = camelize(key);\n    val = getBindAttr(this.el, key);\n    if (val != null) {\n      // dynamic\n      this._setupParamWatcher(mappedKey, val);\n    } else {\n      // static\n      val = getAttr(this.el, key);\n      if (val != null) {\n        this.params[mappedKey] = val === '' ? true : val;\n      }\n    }\n  }\n};\n\n/**\n * Setup a watcher for a dynamic param.\n *\n * @param {String} key\n * @param {String} expression\n */\n\nDirective.prototype._setupParamWatcher = function (key, expression) {\n  var self = this;\n  var called = false;\n  var unwatch = (this._scope || this.vm).$watch(expression, function (val, oldVal) {\n    self.params[key] = val;\n    // since we are in immediate mode,\n    // only call the param change callbacks if this is not the first update.\n    if (called) {\n      var cb = self.paramWatchers && self.paramWatchers[key];\n      if (cb) {\n        cb.call(self, val, oldVal);\n      }\n    } else {\n      called = true;\n    }\n  }, {\n    immediate: true,\n    user: false\n  });(this._paramUnwatchFns || (this._paramUnwatchFns = [])).push(unwatch);\n};\n\n/**\n * Check if the directive is a function caller\n * and if the expression is a callable one. If both true,\n * we wrap up the expression and use it as the event\n * handler.\n *\n * e.g. on-click=\"a++\"\n *\n * @return {Boolean}\n */\n\nDirective.prototype._checkStatement = function () {\n  var expression = this.expression;\n  if (expression && this.acceptStatement && !isSimplePath(expression)) {\n    var fn = parseExpression$1(expression).get;\n    var scope = this._scope || this.vm;\n    var handler = function handler(e) {\n      scope.$event = e;\n      fn.call(scope, scope);\n      scope.$event = null;\n    };\n    if (this.filters) {\n      handler = scope._applyFilters(handler, null, this.filters);\n    }\n    this.update(handler);\n    return true;\n  }\n};\n\n/**\n * Set the corresponding value with the setter.\n * This should only be used in two-way directives\n * e.g. v-model.\n *\n * @param {*} value\n * @public\n */\n\nDirective.prototype.set = function (value) {\n  /* istanbul ignore else */\n  if (this.twoWay) {\n    this._withLock(function () {\n      this._watcher.set(value);\n    });\n  } else if ('production' !== 'production') {}\n};\n\n/**\n * Execute a function while preventing that function from\n * triggering updates on this directive instance.\n *\n * @param {Function} fn\n */\n\nDirective.prototype._withLock = function (fn) {\n  var self = this;\n  self._locked = true;\n  fn.call(self);\n  nextTick(function () {\n    self._locked = false;\n  });\n};\n\n/**\n * Convenience method that attaches a DOM event listener\n * to the directive element and autometically tears it down\n * during unbind.\n *\n * @param {String} event\n * @param {Function} handler\n * @param {Boolean} [useCapture]\n */\n\nDirective.prototype.on = function (event, handler, useCapture) {\n  on(this.el, event, handler, useCapture);(this._listeners || (this._listeners = [])).push([event, handler]);\n};\n\n/**\n * Teardown the watcher and call unbind.\n */\n\nDirective.prototype._teardown = function () {\n  if (this._bound) {\n    this._bound = false;\n    if (this.unbind) {\n      this.unbind();\n    }\n    if (this._watcher) {\n      this._watcher.teardown();\n    }\n    var listeners = this._listeners;\n    var i;\n    if (listeners) {\n      i = listeners.length;\n      while (i--) {\n        off(this.el, listeners[i][0], listeners[i][1]);\n      }\n    }\n    var unwatchFns = this._paramUnwatchFns;\n    if (unwatchFns) {\n      i = unwatchFns.length;\n      while (i--) {\n        unwatchFns[i]();\n      }\n    }\n    if ('production' !== 'production' && this.el) {}\n    this.vm = this.el = this._watcher = this._listeners = null;\n  }\n};\n\nfunction lifecycleMixin (Vue) {\n  /**\n   * Update v-ref for component.\n   *\n   * @param {Boolean} remove\n   */\n\n  Vue.prototype._updateRef = function (remove) {\n    var ref = this.$options._ref;\n    if (ref) {\n      var refs = (this._scope || this._context).$refs;\n      if (remove) {\n        if (refs[ref] === this) {\n          refs[ref] = null;\n        }\n      } else {\n        refs[ref] = this;\n      }\n    }\n  };\n\n  /**\n   * Transclude, compile and link element.\n   *\n   * If a pre-compiled linker is available, that means the\n   * passed in element will be pre-transcluded and compiled\n   * as well - all we need to do is to call the linker.\n   *\n   * Otherwise we need to call transclude/compile/link here.\n   *\n   * @param {Element} el\n   */\n\n  Vue.prototype._compile = function (el) {\n    var options = this.$options;\n\n    // transclude and init element\n    // transclude can potentially replace original\n    // so we need to keep reference; this step also injects\n    // the template and caches the original attributes\n    // on the container node and replacer node.\n    var original = el;\n    el = transclude(el, options);\n    this._initElement(el);\n\n    // handle v-pre on root node (#2026)\n    if (el.nodeType === 1 && getAttr(el, 'v-pre') !== null) {\n      return;\n    }\n\n    // root is always compiled per-instance, because\n    // container attrs and props can be different every time.\n    var contextOptions = this._context && this._context.$options;\n    var rootLinker = compileRoot(el, options, contextOptions);\n\n    // resolve slot distribution\n    resolveSlots(this, options._content);\n\n    // compile and link the rest\n    var contentLinkFn;\n    var ctor = this.constructor;\n    // component compilation can be cached\n    // as long as it's not using inline-template\n    if (options._linkerCachable) {\n      contentLinkFn = ctor.linker;\n      if (!contentLinkFn) {\n        contentLinkFn = ctor.linker = compile(el, options);\n      }\n    }\n\n    // link phase\n    // make sure to link root with prop scope!\n    var rootUnlinkFn = rootLinker(this, el, this._scope);\n    var contentUnlinkFn = contentLinkFn ? contentLinkFn(this, el) : compile(el, options)(this, el);\n\n    // register composite unlink function\n    // to be called during instance destruction\n    this._unlinkFn = function () {\n      rootUnlinkFn();\n      // passing destroying: true to avoid searching and\n      // splicing the directives\n      contentUnlinkFn(true);\n    };\n\n    // finally replace original\n    if (options.replace) {\n      replace(original, el);\n    }\n\n    this._isCompiled = true;\n    this._callHook('compiled');\n  };\n\n  /**\n   * Initialize instance element. Called in the public\n   * $mount() method.\n   *\n   * @param {Element} el\n   */\n\n  Vue.prototype._initElement = function (el) {\n    if (isFragment(el)) {\n      this._isFragment = true;\n      this.$el = this._fragmentStart = el.firstChild;\n      this._fragmentEnd = el.lastChild;\n      // set persisted text anchors to empty\n      if (this._fragmentStart.nodeType === 3) {\n        this._fragmentStart.data = this._fragmentEnd.data = '';\n      }\n      this._fragment = el;\n    } else {\n      this.$el = el;\n    }\n    this.$el.__vue__ = this;\n    this._callHook('beforeCompile');\n  };\n\n  /**\n   * Create and bind a directive to an element.\n   *\n   * @param {Object} descriptor - parsed directive descriptor\n   * @param {Node} node   - target node\n   * @param {Vue} [host] - transclusion host component\n   * @param {Object} [scope] - v-for scope\n   * @param {Fragment} [frag] - owner fragment\n   */\n\n  Vue.prototype._bindDir = function (descriptor, node, host, scope, frag) {\n    this._directives.push(new Directive(descriptor, this, node, host, scope, frag));\n  };\n\n  /**\n   * Teardown an instance, unobserves the data, unbind all the\n   * directives, turn off all the event listeners, etc.\n   *\n   * @param {Boolean} remove - whether to remove the DOM node.\n   * @param {Boolean} deferCleanup - if true, defer cleanup to\n   *                                 be called later\n   */\n\n  Vue.prototype._destroy = function (remove, deferCleanup) {\n    if (this._isBeingDestroyed) {\n      if (!deferCleanup) {\n        this._cleanup();\n      }\n      return;\n    }\n\n    var destroyReady;\n    var pendingRemoval;\n\n    var self = this;\n    // Cleanup should be called either synchronously or asynchronoysly as\n    // callback of this.$remove(), or if remove and deferCleanup are false.\n    // In any case it should be called after all other removing, unbinding and\n    // turning of is done\n    var cleanupIfPossible = function cleanupIfPossible() {\n      if (destroyReady && !pendingRemoval && !deferCleanup) {\n        self._cleanup();\n      }\n    };\n\n    // remove DOM element\n    if (remove && this.$el) {\n      pendingRemoval = true;\n      this.$remove(function () {\n        pendingRemoval = false;\n        cleanupIfPossible();\n      });\n    }\n\n    this._callHook('beforeDestroy');\n    this._isBeingDestroyed = true;\n    var i;\n    // remove self from parent. only necessary\n    // if parent is not being destroyed as well.\n    var parent = this.$parent;\n    if (parent && !parent._isBeingDestroyed) {\n      parent.$children.$remove(this);\n      // unregister ref (remove: true)\n      this._updateRef(true);\n    }\n    // destroy all children.\n    i = this.$children.length;\n    while (i--) {\n      this.$children[i].$destroy();\n    }\n    // teardown props\n    if (this._propsUnlinkFn) {\n      this._propsUnlinkFn();\n    }\n    // teardown all directives. this also tearsdown all\n    // directive-owned watchers.\n    if (this._unlinkFn) {\n      this._unlinkFn();\n    }\n    i = this._watchers.length;\n    while (i--) {\n      this._watchers[i].teardown();\n    }\n    // remove reference to self on $el\n    if (this.$el) {\n      this.$el.__vue__ = null;\n    }\n\n    destroyReady = true;\n    cleanupIfPossible();\n  };\n\n  /**\n   * Clean up to ensure garbage collection.\n   * This is called after the leave transition if there\n   * is any.\n   */\n\n  Vue.prototype._cleanup = function () {\n    if (this._isDestroyed) {\n      return;\n    }\n    // remove self from owner fragment\n    // do it in cleanup so that we can call $destroy with\n    // defer right when a fragment is about to be removed.\n    if (this._frag) {\n      this._frag.children.$remove(this);\n    }\n    // remove reference from data ob\n    // frozen object may not have observer.\n    if (this._data && this._data.__ob__) {\n      this._data.__ob__.removeVm(this);\n    }\n    // Clean up references to private properties and other\n    // instances. preserve reference to _data so that proxy\n    // accessors still work. The only potential side effect\n    // here is that mutating the instance after it's destroyed\n    // may affect the state of other components that are still\n    // observing the same object, but that seems to be a\n    // reasonable responsibility for the user rather than\n    // always throwing an error on them.\n    this.$el = this.$parent = this.$root = this.$children = this._watchers = this._context = this._scope = this._directives = null;\n    // call the last hook...\n    this._isDestroyed = true;\n    this._callHook('destroyed');\n    // turn off all instance listeners.\n    this.$off();\n  };\n}\n\nfunction miscMixin (Vue) {\n  /**\n   * Apply a list of filter (descriptors) to a value.\n   * Using plain for loops here because this will be called in\n   * the getter of any watcher with filters so it is very\n   * performance sensitive.\n   *\n   * @param {*} value\n   * @param {*} [oldValue]\n   * @param {Array} filters\n   * @param {Boolean} write\n   * @return {*}\n   */\n\n  Vue.prototype._applyFilters = function (value, oldValue, filters, write) {\n    var filter, fn, args, arg, offset, i, l, j, k;\n    for (i = 0, l = filters.length; i < l; i++) {\n      filter = filters[write ? l - i - 1 : i];\n      fn = resolveAsset(this.$options, 'filters', filter.name, true);\n      if (!fn) continue;\n      fn = write ? fn.write : fn.read || fn;\n      if (typeof fn !== 'function') continue;\n      args = write ? [value, oldValue] : [value];\n      offset = write ? 2 : 1;\n      if (filter.args) {\n        for (j = 0, k = filter.args.length; j < k; j++) {\n          arg = filter.args[j];\n          args[j + offset] = arg.dynamic ? this.$get(arg.value) : arg.value;\n        }\n      }\n      value = fn.apply(this, args);\n    }\n    return value;\n  };\n\n  /**\n   * Resolve a component, depending on whether the component\n   * is defined normally or using an async factory function.\n   * Resolves synchronously if already resolved, otherwise\n   * resolves asynchronously and caches the resolved\n   * constructor on the factory.\n   *\n   * @param {String|Function} value\n   * @param {Function} cb\n   */\n\n  Vue.prototype._resolveComponent = function (value, cb) {\n    var factory;\n    if (typeof value === 'function') {\n      factory = value;\n    } else {\n      factory = resolveAsset(this.$options, 'components', value, true);\n    }\n    /* istanbul ignore if */\n    if (!factory) {\n      return;\n    }\n    // async component factory\n    if (!factory.options) {\n      if (factory.resolved) {\n        // cached\n        cb(factory.resolved);\n      } else if (factory.requested) {\n        // pool callbacks\n        factory.pendingCallbacks.push(cb);\n      } else {\n        factory.requested = true;\n        var cbs = factory.pendingCallbacks = [cb];\n        factory.call(this, function resolve(res) {\n          if (isPlainObject(res)) {\n            res = Vue.extend(res);\n          }\n          // cache resolved\n          factory.resolved = res;\n          // invoke callbacks\n          for (var i = 0, l = cbs.length; i < l; i++) {\n            cbs[i](res);\n          }\n        }, function reject(reason) {\n          'production' !== 'production' && warn('Failed to resolve async component' + (typeof value === 'string' ? ': ' + value : '') + '. ' + (reason ? '\\nReason: ' + reason : ''));\n        });\n      }\n    } else {\n      // normal component\n      cb(factory);\n    }\n  };\n}\n\nvar filterRE$1 = /[^|]\\|[^|]/;\n\nfunction dataAPI (Vue) {\n  /**\n   * Get the value from an expression on this vm.\n   *\n   * @param {String} exp\n   * @param {Boolean} [asStatement]\n   * @return {*}\n   */\n\n  Vue.prototype.$get = function (exp, asStatement) {\n    var res = parseExpression$1(exp);\n    if (res) {\n      if (asStatement) {\n        var self = this;\n        return function statementHandler() {\n          self.$arguments = toArray(arguments);\n          var result = res.get.call(self, self);\n          self.$arguments = null;\n          return result;\n        };\n      } else {\n        try {\n          return res.get.call(this, this);\n        } catch (e) {}\n      }\n    }\n  };\n\n  /**\n   * Set the value from an expression on this vm.\n   * The expression must be a valid left-hand\n   * expression in an assignment.\n   *\n   * @param {String} exp\n   * @param {*} val\n   */\n\n  Vue.prototype.$set = function (exp, val) {\n    var res = parseExpression$1(exp, true);\n    if (res && res.set) {\n      res.set.call(this, this, val);\n    }\n  };\n\n  /**\n   * Delete a property on the VM\n   *\n   * @param {String} key\n   */\n\n  Vue.prototype.$delete = function (key) {\n    del(this._data, key);\n  };\n\n  /**\n   * Watch an expression, trigger callback when its\n   * value changes.\n   *\n   * @param {String|Function} expOrFn\n   * @param {Function} cb\n   * @param {Object} [options]\n   *                 - {Boolean} deep\n   *                 - {Boolean} immediate\n   * @return {Function} - unwatchFn\n   */\n\n  Vue.prototype.$watch = function (expOrFn, cb, options) {\n    var vm = this;\n    var parsed;\n    if (typeof expOrFn === 'string') {\n      parsed = parseDirective(expOrFn);\n      expOrFn = parsed.expression;\n    }\n    var watcher = new Watcher(vm, expOrFn, cb, {\n      deep: options && options.deep,\n      sync: options && options.sync,\n      filters: parsed && parsed.filters,\n      user: !options || options.user !== false\n    });\n    if (options && options.immediate) {\n      cb.call(vm, watcher.value);\n    }\n    return function unwatchFn() {\n      watcher.teardown();\n    };\n  };\n\n  /**\n   * Evaluate a text directive, including filters.\n   *\n   * @param {String} text\n   * @param {Boolean} [asStatement]\n   * @return {String}\n   */\n\n  Vue.prototype.$eval = function (text, asStatement) {\n    // check for filters.\n    if (filterRE$1.test(text)) {\n      var dir = parseDirective(text);\n      // the filter regex check might give false positive\n      // for pipes inside strings, so it's possible that\n      // we don't get any filters here\n      var val = this.$get(dir.expression, asStatement);\n      return dir.filters ? this._applyFilters(val, null, dir.filters) : val;\n    } else {\n      // no filter\n      return this.$get(text, asStatement);\n    }\n  };\n\n  /**\n   * Interpolate a piece of template text.\n   *\n   * @param {String} text\n   * @return {String}\n   */\n\n  Vue.prototype.$interpolate = function (text) {\n    var tokens = parseText(text);\n    var vm = this;\n    if (tokens) {\n      if (tokens.length === 1) {\n        return vm.$eval(tokens[0].value) + '';\n      } else {\n        return tokens.map(function (token) {\n          return token.tag ? vm.$eval(token.value) : token.value;\n        }).join('');\n      }\n    } else {\n      return text;\n    }\n  };\n\n  /**\n   * Log instance data as a plain JS object\n   * so that it is easier to inspect in console.\n   * This method assumes console is available.\n   *\n   * @param {String} [path]\n   */\n\n  Vue.prototype.$log = function (path) {\n    var data = path ? getPath(this._data, path) : this._data;\n    if (data) {\n      data = clean(data);\n    }\n    // include computed fields\n    if (!path) {\n      var key;\n      for (key in this.$options.computed) {\n        data[key] = clean(this[key]);\n      }\n      if (this._props) {\n        for (key in this._props) {\n          data[key] = clean(this[key]);\n        }\n      }\n    }\n    console.log(data);\n  };\n\n  /**\n   * \"clean\" a getter/setter converted object into a plain\n   * object copy.\n   *\n   * @param {Object} - obj\n   * @return {Object}\n   */\n\n  function clean(obj) {\n    return JSON.parse(JSON.stringify(obj));\n  }\n}\n\nfunction domAPI (Vue) {\n  /**\n   * Convenience on-instance nextTick. The callback is\n   * auto-bound to the instance, and this avoids component\n   * modules having to rely on the global Vue.\n   *\n   * @param {Function} fn\n   */\n\n  Vue.prototype.$nextTick = function (fn) {\n    nextTick(fn, this);\n  };\n\n  /**\n   * Append instance to target\n   *\n   * @param {Node} target\n   * @param {Function} [cb]\n   * @param {Boolean} [withTransition] - defaults to true\n   */\n\n  Vue.prototype.$appendTo = function (target, cb, withTransition) {\n    return insert(this, target, cb, withTransition, append, appendWithTransition);\n  };\n\n  /**\n   * Prepend instance to target\n   *\n   * @param {Node} target\n   * @param {Function} [cb]\n   * @param {Boolean} [withTransition] - defaults to true\n   */\n\n  Vue.prototype.$prependTo = function (target, cb, withTransition) {\n    target = query(target);\n    if (target.hasChildNodes()) {\n      this.$before(target.firstChild, cb, withTransition);\n    } else {\n      this.$appendTo(target, cb, withTransition);\n    }\n    return this;\n  };\n\n  /**\n   * Insert instance before target\n   *\n   * @param {Node} target\n   * @param {Function} [cb]\n   * @param {Boolean} [withTransition] - defaults to true\n   */\n\n  Vue.prototype.$before = function (target, cb, withTransition) {\n    return insert(this, target, cb, withTransition, beforeWithCb, beforeWithTransition);\n  };\n\n  /**\n   * Insert instance after target\n   *\n   * @param {Node} target\n   * @param {Function} [cb]\n   * @param {Boolean} [withTransition] - defaults to true\n   */\n\n  Vue.prototype.$after = function (target, cb, withTransition) {\n    target = query(target);\n    if (target.nextSibling) {\n      this.$before(target.nextSibling, cb, withTransition);\n    } else {\n      this.$appendTo(target.parentNode, cb, withTransition);\n    }\n    return this;\n  };\n\n  /**\n   * Remove instance from DOM\n   *\n   * @param {Function} [cb]\n   * @param {Boolean} [withTransition] - defaults to true\n   */\n\n  Vue.prototype.$remove = function (cb, withTransition) {\n    if (!this.$el.parentNode) {\n      return cb && cb();\n    }\n    var inDocument = this._isAttached && inDoc(this.$el);\n    // if we are not in document, no need to check\n    // for transitions\n    if (!inDocument) withTransition = false;\n    var self = this;\n    var realCb = function realCb() {\n      if (inDocument) self._callHook('detached');\n      if (cb) cb();\n    };\n    if (this._isFragment) {\n      removeNodeRange(this._fragmentStart, this._fragmentEnd, this, this._fragment, realCb);\n    } else {\n      var op = withTransition === false ? removeWithCb : removeWithTransition;\n      op(this.$el, this, realCb);\n    }\n    return this;\n  };\n\n  /**\n   * Shared DOM insertion function.\n   *\n   * @param {Vue} vm\n   * @param {Element} target\n   * @param {Function} [cb]\n   * @param {Boolean} [withTransition]\n   * @param {Function} op1 - op for non-transition insert\n   * @param {Function} op2 - op for transition insert\n   * @return vm\n   */\n\n  function insert(vm, target, cb, withTransition, op1, op2) {\n    target = query(target);\n    var targetIsDetached = !inDoc(target);\n    var op = withTransition === false || targetIsDetached ? op1 : op2;\n    var shouldCallHook = !targetIsDetached && !vm._isAttached && !inDoc(vm.$el);\n    if (vm._isFragment) {\n      mapNodeRange(vm._fragmentStart, vm._fragmentEnd, function (node) {\n        op(node, target, vm);\n      });\n      cb && cb();\n    } else {\n      op(vm.$el, target, vm, cb);\n    }\n    if (shouldCallHook) {\n      vm._callHook('attached');\n    }\n    return vm;\n  }\n\n  /**\n   * Check for selectors\n   *\n   * @param {String|Element} el\n   */\n\n  function query(el) {\n    return typeof el === 'string' ? document.querySelector(el) : el;\n  }\n\n  /**\n   * Append operation that takes a callback.\n   *\n   * @param {Node} el\n   * @param {Node} target\n   * @param {Vue} vm - unused\n   * @param {Function} [cb]\n   */\n\n  function append(el, target, vm, cb) {\n    target.appendChild(el);\n    if (cb) cb();\n  }\n\n  /**\n   * InsertBefore operation that takes a callback.\n   *\n   * @param {Node} el\n   * @param {Node} target\n   * @param {Vue} vm - unused\n   * @param {Function} [cb]\n   */\n\n  function beforeWithCb(el, target, vm, cb) {\n    before(el, target);\n    if (cb) cb();\n  }\n\n  /**\n   * Remove operation that takes a callback.\n   *\n   * @param {Node} el\n   * @param {Vue} vm - unused\n   * @param {Function} [cb]\n   */\n\n  function removeWithCb(el, vm, cb) {\n    remove(el);\n    if (cb) cb();\n  }\n}\n\nfunction eventsAPI (Vue) {\n  /**\n   * Listen on the given `event` with `fn`.\n   *\n   * @param {String} event\n   * @param {Function} fn\n   */\n\n  Vue.prototype.$on = function (event, fn) {\n    (this._events[event] || (this._events[event] = [])).push(fn);\n    modifyListenerCount(this, event, 1);\n    return this;\n  };\n\n  /**\n   * Adds an `event` listener that will be invoked a single\n   * time then automatically removed.\n   *\n   * @param {String} event\n   * @param {Function} fn\n   */\n\n  Vue.prototype.$once = function (event, fn) {\n    var self = this;\n    function on() {\n      self.$off(event, on);\n      fn.apply(this, arguments);\n    }\n    on.fn = fn;\n    this.$on(event, on);\n    return this;\n  };\n\n  /**\n   * Remove the given callback for `event` or all\n   * registered callbacks.\n   *\n   * @param {String} event\n   * @param {Function} fn\n   */\n\n  Vue.prototype.$off = function (event, fn) {\n    var cbs;\n    // all\n    if (!arguments.length) {\n      if (this.$parent) {\n        for (event in this._events) {\n          cbs = this._events[event];\n          if (cbs) {\n            modifyListenerCount(this, event, -cbs.length);\n          }\n        }\n      }\n      this._events = {};\n      return this;\n    }\n    // specific event\n    cbs = this._events[event];\n    if (!cbs) {\n      return this;\n    }\n    if (arguments.length === 1) {\n      modifyListenerCount(this, event, -cbs.length);\n      this._events[event] = null;\n      return this;\n    }\n    // specific handler\n    var cb;\n    var i = cbs.length;\n    while (i--) {\n      cb = cbs[i];\n      if (cb === fn || cb.fn === fn) {\n        modifyListenerCount(this, event, -1);\n        cbs.splice(i, 1);\n        break;\n      }\n    }\n    return this;\n  };\n\n  /**\n   * Trigger an event on self.\n   *\n   * @param {String|Object} event\n   * @return {Boolean} shouldPropagate\n   */\n\n  Vue.prototype.$emit = function (event) {\n    var isSource = typeof event === 'string';\n    event = isSource ? event : event.name;\n    var cbs = this._events[event];\n    var shouldPropagate = isSource || !cbs;\n    if (cbs) {\n      cbs = cbs.length > 1 ? toArray(cbs) : cbs;\n      // this is a somewhat hacky solution to the question raised\n      // in #2102: for an inline component listener like <comp @test=\"doThis\">,\n      // the propagation handling is somewhat broken. Therefore we\n      // need to treat these inline callbacks differently.\n      var hasParentCbs = isSource && cbs.some(function (cb) {\n        return cb._fromParent;\n      });\n      if (hasParentCbs) {\n        shouldPropagate = false;\n      }\n      var args = toArray(arguments, 1);\n      for (var i = 0, l = cbs.length; i < l; i++) {\n        var cb = cbs[i];\n        var res = cb.apply(this, args);\n        if (res === true && (!hasParentCbs || cb._fromParent)) {\n          shouldPropagate = true;\n        }\n      }\n    }\n    return shouldPropagate;\n  };\n\n  /**\n   * Recursively broadcast an event to all children instances.\n   *\n   * @param {String|Object} event\n   * @param {...*} additional arguments\n   */\n\n  Vue.prototype.$broadcast = function (event) {\n    var isSource = typeof event === 'string';\n    event = isSource ? event : event.name;\n    // if no child has registered for this event,\n    // then there's no need to broadcast.\n    if (!this._eventsCount[event]) return;\n    var children = this.$children;\n    var args = toArray(arguments);\n    if (isSource) {\n      // use object event to indicate non-source emit\n      // on children\n      args[0] = { name: event, source: this };\n    }\n    for (var i = 0, l = children.length; i < l; i++) {\n      var child = children[i];\n      var shouldPropagate = child.$emit.apply(child, args);\n      if (shouldPropagate) {\n        child.$broadcast.apply(child, args);\n      }\n    }\n    return this;\n  };\n\n  /**\n   * Recursively propagate an event up the parent chain.\n   *\n   * @param {String} event\n   * @param {...*} additional arguments\n   */\n\n  Vue.prototype.$dispatch = function (event) {\n    var shouldPropagate = this.$emit.apply(this, arguments);\n    if (!shouldPropagate) return;\n    var parent = this.$parent;\n    var args = toArray(arguments);\n    // use object event to indicate non-source emit\n    // on parents\n    args[0] = { name: event, source: this };\n    while (parent) {\n      shouldPropagate = parent.$emit.apply(parent, args);\n      parent = shouldPropagate ? parent.$parent : null;\n    }\n    return this;\n  };\n\n  /**\n   * Modify the listener counts on all parents.\n   * This bookkeeping allows $broadcast to return early when\n   * no child has listened to a certain event.\n   *\n   * @param {Vue} vm\n   * @param {String} event\n   * @param {Number} count\n   */\n\n  var hookRE = /^hook:/;\n  function modifyListenerCount(vm, event, count) {\n    var parent = vm.$parent;\n    // hooks do not get broadcasted so no need\n    // to do bookkeeping for them\n    if (!parent || !count || hookRE.test(event)) return;\n    while (parent) {\n      parent._eventsCount[event] = (parent._eventsCount[event] || 0) + count;\n      parent = parent.$parent;\n    }\n  }\n}\n\nfunction lifecycleAPI (Vue) {\n  /**\n   * Set instance target element and kick off the compilation\n   * process. The passed in `el` can be a selector string, an\n   * existing Element, or a DocumentFragment (for block\n   * instances).\n   *\n   * @param {Element|DocumentFragment|string} el\n   * @public\n   */\n\n  Vue.prototype.$mount = function (el) {\n    if (this._isCompiled) {\n      'production' !== 'production' && warn('$mount() should be called only once.', this);\n      return;\n    }\n    el = query(el);\n    if (!el) {\n      el = document.createElement('div');\n    }\n    this._compile(el);\n    this._initDOMHooks();\n    if (inDoc(this.$el)) {\n      this._callHook('attached');\n      ready.call(this);\n    } else {\n      this.$once('hook:attached', ready);\n    }\n    return this;\n  };\n\n  /**\n   * Mark an instance as ready.\n   */\n\n  function ready() {\n    this._isAttached = true;\n    this._isReady = true;\n    this._callHook('ready');\n  }\n\n  /**\n   * Teardown the instance, simply delegate to the internal\n   * _destroy.\n   *\n   * @param {Boolean} remove\n   * @param {Boolean} deferCleanup\n   */\n\n  Vue.prototype.$destroy = function (remove, deferCleanup) {\n    this._destroy(remove, deferCleanup);\n  };\n\n  /**\n   * Partially compile a piece of DOM and return a\n   * decompile function.\n   *\n   * @param {Element|DocumentFragment} el\n   * @param {Vue} [host]\n   * @param {Object} [scope]\n   * @param {Fragment} [frag]\n   * @return {Function}\n   */\n\n  Vue.prototype.$compile = function (el, host, scope, frag) {\n    return compile(el, this.$options, true)(this, el, host, scope, frag);\n  };\n}\n\n/**\n * The exposed Vue constructor.\n *\n * API conventions:\n * - public API methods/properties are prefixed with `$`\n * - internal methods/properties are prefixed with `_`\n * - non-prefixed properties are assumed to be proxied user\n *   data.\n *\n * @constructor\n * @param {Object} [options]\n * @public\n */\n\nfunction Vue(options) {\n  this._init(options);\n}\n\n// install internals\ninitMixin(Vue);\nstateMixin(Vue);\neventsMixin(Vue);\nlifecycleMixin(Vue);\nmiscMixin(Vue);\n\n// install instance APIs\ndataAPI(Vue);\ndomAPI(Vue);\neventsAPI(Vue);\nlifecycleAPI(Vue);\n\nvar slot = {\n\n  priority: SLOT,\n  params: ['name'],\n\n  bind: function bind() {\n    // this was resolved during component transclusion\n    var name = this.params.name || 'default';\n    var content = this.vm._slotContents && this.vm._slotContents[name];\n    if (!content || !content.hasChildNodes()) {\n      this.fallback();\n    } else {\n      this.compile(content.cloneNode(true), this.vm._context, this.vm);\n    }\n  },\n\n  compile: function compile(content, context, host) {\n    if (content && context) {\n      if (this.el.hasChildNodes() && content.childNodes.length === 1 && content.childNodes[0].nodeType === 1 && content.childNodes[0].hasAttribute('v-if')) {\n        // if the inserted slot has v-if\n        // inject fallback content as the v-else\n        var elseBlock = document.createElement('template');\n        elseBlock.setAttribute('v-else', '');\n        elseBlock.innerHTML = this.el.innerHTML;\n        // the else block should be compiled in child scope\n        elseBlock._context = this.vm;\n        content.appendChild(elseBlock);\n      }\n      var scope = host ? host._scope : this._scope;\n      this.unlink = context.$compile(content, host, scope, this._frag);\n    }\n    if (content) {\n      replace(this.el, content);\n    } else {\n      remove(this.el);\n    }\n  },\n\n  fallback: function fallback() {\n    this.compile(extractContent(this.el, true), this.vm);\n  },\n\n  unbind: function unbind() {\n    if (this.unlink) {\n      this.unlink();\n    }\n  }\n};\n\nvar partial = {\n\n  priority: PARTIAL,\n\n  params: ['name'],\n\n  // watch changes to name for dynamic partials\n  paramWatchers: {\n    name: function name(value) {\n      vIf.remove.call(this);\n      if (value) {\n        this.insert(value);\n      }\n    }\n  },\n\n  bind: function bind() {\n    this.anchor = createAnchor('v-partial');\n    replace(this.el, this.anchor);\n    this.insert(this.params.name);\n  },\n\n  insert: function insert(id) {\n    var partial = resolveAsset(this.vm.$options, 'partials', id, true);\n    if (partial) {\n      this.factory = new FragmentFactory(this.vm, partial);\n      vIf.insert.call(this);\n    }\n  },\n\n  unbind: function unbind() {\n    if (this.frag) {\n      this.frag.destroy();\n    }\n  }\n};\n\nvar elementDirectives = {\n  slot: slot,\n  partial: partial\n};\n\nvar convertArray = vFor._postProcess;\n\n/**\n * Limit filter for arrays\n *\n * @param {Number} n\n * @param {Number} offset (Decimal expected)\n */\n\nfunction limitBy(arr, n, offset) {\n  offset = offset ? parseInt(offset, 10) : 0;\n  n = toNumber(n);\n  return typeof n === 'number' ? arr.slice(offset, offset + n) : arr;\n}\n\n/**\n * Filter filter for arrays\n *\n * @param {String} search\n * @param {String} [delimiter]\n * @param {String} ...dataKeys\n */\n\nfunction filterBy(arr, search, delimiter) {\n  arr = convertArray(arr);\n  if (search == null) {\n    return arr;\n  }\n  if (typeof search === 'function') {\n    return arr.filter(search);\n  }\n  // cast to lowercase string\n  search = ('' + search).toLowerCase();\n  // allow optional `in` delimiter\n  // because why not\n  var n = delimiter === 'in' ? 3 : 2;\n  // extract and flatten keys\n  var keys = Array.prototype.concat.apply([], toArray(arguments, n));\n  var res = [];\n  var item, key, val, j;\n  for (var i = 0, l = arr.length; i < l; i++) {\n    item = arr[i];\n    val = item && item.$value || item;\n    j = keys.length;\n    if (j) {\n      while (j--) {\n        key = keys[j];\n        if (key === '$key' && contains(item.$key, search) || contains(getPath(val, key), search)) {\n          res.push(item);\n          break;\n        }\n      }\n    } else if (contains(item, search)) {\n      res.push(item);\n    }\n  }\n  return res;\n}\n\n/**\n * Order filter for arrays\n *\n * @param {String|Array<String>|Function} ...sortKeys\n * @param {Number} [order]\n */\n\nfunction orderBy(arr) {\n  var comparator = null;\n  var sortKeys = undefined;\n  arr = convertArray(arr);\n\n  // determine order (last argument)\n  var args = toArray(arguments, 1);\n  var order = args[args.length - 1];\n  if (typeof order === 'number') {\n    order = order < 0 ? -1 : 1;\n    args = args.length > 1 ? args.slice(0, -1) : args;\n  } else {\n    order = 1;\n  }\n\n  // determine sortKeys & comparator\n  var firstArg = args[0];\n  if (!firstArg) {\n    return arr;\n  } else if (typeof firstArg === 'function') {\n    // custom comparator\n    comparator = function (a, b) {\n      return firstArg(a, b) * order;\n    };\n  } else {\n    // string keys. flatten first\n    sortKeys = Array.prototype.concat.apply([], args);\n    comparator = function (a, b, i) {\n      i = i || 0;\n      return i >= sortKeys.length - 1 ? baseCompare(a, b, i) : baseCompare(a, b, i) || comparator(a, b, i + 1);\n    };\n  }\n\n  function baseCompare(a, b, sortKeyIndex) {\n    var sortKey = sortKeys[sortKeyIndex];\n    if (sortKey) {\n      if (sortKey !== '$key') {\n        if (isObject(a) && '$value' in a) a = a.$value;\n        if (isObject(b) && '$value' in b) b = b.$value;\n      }\n      a = isObject(a) ? getPath(a, sortKey) : a;\n      b = isObject(b) ? getPath(b, sortKey) : b;\n    }\n    return a === b ? 0 : a > b ? order : -order;\n  }\n\n  // sort on a copy to avoid mutating original array\n  return arr.slice().sort(comparator);\n}\n\n/**\n * String contain helper\n *\n * @param {*} val\n * @param {String} search\n */\n\nfunction contains(val, search) {\n  var i;\n  if (isPlainObject(val)) {\n    var keys = Object.keys(val);\n    i = keys.length;\n    while (i--) {\n      if (contains(val[keys[i]], search)) {\n        return true;\n      }\n    }\n  } else if (isArray(val)) {\n    i = val.length;\n    while (i--) {\n      if (contains(val[i], search)) {\n        return true;\n      }\n    }\n  } else if (val != null) {\n    return val.toString().toLowerCase().indexOf(search) > -1;\n  }\n}\n\nvar digitsRE = /(\\d{3})(?=\\d)/g;\n\n// asset collections must be a plain object.\nvar filters = {\n\n  orderBy: orderBy,\n  filterBy: filterBy,\n  limitBy: limitBy,\n\n  /**\n   * Stringify value.\n   *\n   * @param {Number} indent\n   */\n\n  json: {\n    read: function read(value, indent) {\n      return typeof value === 'string' ? value : JSON.stringify(value, null, arguments.length > 1 ? indent : 2);\n    },\n    write: function write(value) {\n      try {\n        return JSON.parse(value);\n      } catch (e) {\n        return value;\n      }\n    }\n  },\n\n  /**\n   * 'abc' => 'Abc'\n   */\n\n  capitalize: function capitalize(value) {\n    if (!value && value !== 0) return '';\n    value = value.toString();\n    return value.charAt(0).toUpperCase() + value.slice(1);\n  },\n\n  /**\n   * 'abc' => 'ABC'\n   */\n\n  uppercase: function uppercase(value) {\n    return value || value === 0 ? value.toString().toUpperCase() : '';\n  },\n\n  /**\n   * 'AbC' => 'abc'\n   */\n\n  lowercase: function lowercase(value) {\n    return value || value === 0 ? value.toString().toLowerCase() : '';\n  },\n\n  /**\n   * 12345 => $12,345.00\n   *\n   * @param {String} sign\n   * @param {Number} decimals Decimal places\n   */\n\n  currency: function currency(value, _currency, decimals) {\n    value = parseFloat(value);\n    if (!isFinite(value) || !value && value !== 0) return '';\n    _currency = _currency != null ? _currency : '$';\n    decimals = decimals != null ? decimals : 2;\n    var stringified = Math.abs(value).toFixed(decimals);\n    var _int = decimals ? stringified.slice(0, -1 - decimals) : stringified;\n    var i = _int.length % 3;\n    var head = i > 0 ? _int.slice(0, i) + (_int.length > 3 ? ',' : '') : '';\n    var _float = decimals ? stringified.slice(-1 - decimals) : '';\n    var sign = value < 0 ? '-' : '';\n    return sign + _currency + head + _int.slice(i).replace(digitsRE, '$1,') + _float;\n  },\n\n  /**\n   * 'item' => 'items'\n   *\n   * @params\n   *  an array of strings corresponding to\n   *  the single, double, triple ... forms of the word to\n   *  be pluralized. When the number to be pluralized\n   *  exceeds the length of the args, it will use the last\n   *  entry in the array.\n   *\n   *  e.g. ['single', 'double', 'triple', 'multiple']\n   */\n\n  pluralize: function pluralize(value) {\n    var args = toArray(arguments, 1);\n    var length = args.length;\n    if (length > 1) {\n      var index = value % 10 - 1;\n      return index in args ? args[index] : args[length - 1];\n    } else {\n      return args[0] + (value === 1 ? '' : 's');\n    }\n  },\n\n  /**\n   * Debounce a handler function.\n   *\n   * @param {Function} handler\n   * @param {Number} delay = 300\n   * @return {Function}\n   */\n\n  debounce: function debounce(handler, delay) {\n    if (!handler) return;\n    if (!delay) {\n      delay = 300;\n    }\n    return _debounce(handler, delay);\n  }\n};\n\nfunction installGlobalAPI (Vue) {\n  /**\n   * Vue and every constructor that extends Vue has an\n   * associated options object, which can be accessed during\n   * compilation steps as `this.constructor.options`.\n   *\n   * These can be seen as the default options of every\n   * Vue instance.\n   */\n\n  Vue.options = {\n    directives: directives,\n    elementDirectives: elementDirectives,\n    filters: filters,\n    transitions: {},\n    components: {},\n    partials: {},\n    replace: true\n  };\n\n  /**\n   * Expose useful internals\n   */\n\n  Vue.util = util;\n  Vue.config = config;\n  Vue.set = set;\n  Vue['delete'] = del;\n  Vue.nextTick = nextTick;\n\n  /**\n   * The following are exposed for advanced usage / plugins\n   */\n\n  Vue.compiler = compiler;\n  Vue.FragmentFactory = FragmentFactory;\n  Vue.internalDirectives = internalDirectives;\n  Vue.parsers = {\n    path: path,\n    text: text,\n    template: template,\n    directive: directive,\n    expression: expression\n  };\n\n  /**\n   * Each instance constructor, including Vue, has a unique\n   * cid. This enables us to create wrapped \"child\n   * constructors\" for prototypal inheritance and cache them.\n   */\n\n  Vue.cid = 0;\n  var cid = 1;\n\n  /**\n   * Class inheritance\n   *\n   * @param {Object} extendOptions\n   */\n\n  Vue.extend = function (extendOptions) {\n    extendOptions = extendOptions || {};\n    var Super = this;\n    var isFirstExtend = Super.cid === 0;\n    if (isFirstExtend && extendOptions._Ctor) {\n      return extendOptions._Ctor;\n    }\n    var name = extendOptions.name || Super.options.name;\n    if ('production' !== 'production') {}\n    var Sub = createClass(name || 'VueComponent');\n    Sub.prototype = Object.create(Super.prototype);\n    Sub.prototype.constructor = Sub;\n    Sub.cid = cid++;\n    Sub.options = mergeOptions(Super.options, extendOptions);\n    Sub['super'] = Super;\n    // allow further extension\n    Sub.extend = Super.extend;\n    // create asset registers, so extended classes\n    // can have their private assets too.\n    config._assetTypes.forEach(function (type) {\n      Sub[type] = Super[type];\n    });\n    // enable recursive self-lookup\n    if (name) {\n      Sub.options.components[name] = Sub;\n    }\n    // cache constructor\n    if (isFirstExtend) {\n      extendOptions._Ctor = Sub;\n    }\n    return Sub;\n  };\n\n  /**\n   * A function that returns a sub-class constructor with the\n   * given name. This gives us much nicer output when\n   * logging instances in the console.\n   *\n   * @param {String} name\n   * @return {Function}\n   */\n\n  function createClass(name) {\n    /* eslint-disable no-new-func */\n    return new Function('return function ' + classify(name) + ' (options) { this._init(options) }')();\n    /* eslint-enable no-new-func */\n  }\n\n  /**\n   * Plugin system\n   *\n   * @param {Object} plugin\n   */\n\n  Vue.use = function (plugin) {\n    /* istanbul ignore if */\n    if (plugin.installed) {\n      return;\n    }\n    // additional parameters\n    var args = toArray(arguments, 1);\n    args.unshift(this);\n    if (typeof plugin.install === 'function') {\n      plugin.install.apply(plugin, args);\n    } else {\n      plugin.apply(null, args);\n    }\n    plugin.installed = true;\n    return this;\n  };\n\n  /**\n   * Apply a global mixin by merging it into the default\n   * options.\n   */\n\n  Vue.mixin = function (mixin) {\n    Vue.options = mergeOptions(Vue.options, mixin);\n  };\n\n  /**\n   * Create asset registration methods with the following\n   * signature:\n   *\n   * @param {String} id\n   * @param {*} definition\n   */\n\n  config._assetTypes.forEach(function (type) {\n    Vue[type] = function (id, definition) {\n      if (!definition) {\n        return this.options[type + 's'][id];\n      } else {\n        /* istanbul ignore if */\n        if ('production' !== 'production') {}\n        if (type === 'component' && isPlainObject(definition)) {\n          if (!definition.name) {\n            definition.name = id;\n          }\n          definition = Vue.extend(definition);\n        }\n        this.options[type + 's'][id] = definition;\n        return definition;\n      }\n    };\n  });\n\n  // expose internal transition API\n  extend(Vue.transition, transition);\n}\n\ninstallGlobalAPI(Vue);\n\nVue.version = '1.0.28';\n\n// devtools global hook\n/* istanbul ignore next */\nsetTimeout(function () {\n  if (config.devtools) {\n    if (devtools) {\n      devtools.emit('init', Vue);\n    } else if ('production' !== 'production' && inBrowser && /Chrome\\/\\d+/.test(window.navigator.userAgent)) {}\n  }\n}, 0);\n\nreturn Vue;\n\n})));"]}
\ No newline at end of file
diff --git a/dist/vue.runtime.common.js b/dist/vue.runtime.common.js
deleted file mode 100644
index 07ddaa2ccba..00000000000
--- a/dist/vue.runtime.common.js
+++ /dev/null
@@ -1,5 +0,0 @@
-if (process.env.NODE_ENV === 'production') {
-  module.exports = require('./vue.runtime.common.prod.js')
-} else {
-  module.exports = require('./vue.runtime.common.dev.js')
-}
diff --git a/dist/vue.runtime.mjs b/dist/vue.runtime.mjs
deleted file mode 100644
index 83d889271bd..00000000000
--- a/dist/vue.runtime.mjs
+++ /dev/null
@@ -1,76 +0,0 @@
-import Vue from './vue.runtime.common.js'
-export default Vue
-
-// this should be kept in sync with src/v3/index.ts
-export const {
-  version,
-
-  // refs
-  ref,
-  shallowRef,
-  isRef,
-  toRef,
-  toRefs,
-  unref,
-  proxyRefs,
-  customRef,
-  triggerRef,
-  computed,
-
-  // reactive
-  reactive,
-  isReactive,
-  isReadonly,
-  isShallow,
-  isProxy,
-  shallowReactive,
-  markRaw,
-  toRaw,
-  readonly,
-  shallowReadonly,
-
-  // watch
-  watch,
-  watchEffect,
-  watchPostEffect,
-  watchSyncEffect,
-
-  // effectScope
-  effectScope,
-  onScopeDispose,
-  getCurrentScope,
-
-  // provide / inject
-  provide,
-  inject,
-
-  // lifecycle
-  onBeforeMount,
-  onMounted,
-  onBeforeUpdate,
-  onUpdated,
-  onBeforeUnmount,
-  onUnmounted,
-  onErrorCaptured,
-  onActivated,
-  onDeactivated,
-  onServerPrefetch,
-  onRenderTracked,
-  onRenderTriggered,
-
-  // v2 only
-  set,
-  del,
-
-  // v3 compat
-  h,
-  getCurrentInstance,
-  useSlots,
-  useAttrs,
-  mergeDefaults,
-  nextTick,
-  useCssModule,
-  useCssVars,
-  defineComponent,
-  defineAsyncComponent
-} = Vue
diff --git a/examples/classic/commits/app.js b/examples/classic/commits/app.js
deleted file mode 100644
index d15ec443cd9..00000000000
--- a/examples/classic/commits/app.js
+++ /dev/null
@@ -1,46 +0,0 @@
-var apiURL = 'https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha='
-
-/**
- * Actual demo
- */
-
-new Vue({
-  el: '#demo',
-
-  data: {
-    branches: ['main', 'dev'],
-    currentBranch: 'main',
-    commits: null
-  },
-
-  created: function () {
-    this.fetchData()
-  },
-
-  watch: {
-    currentBranch: 'fetchData'
-  },
-
-  filters: {
-    truncate: function (v) {
-      var newline = v.indexOf('\n')
-      return newline > 0 ? v.slice(0, newline) : v
-    },
-    formatDate: function (v) {
-      return v.replace(/T|Z/g, ' ')
-    }
-  },
-
-  methods: {
-    fetchData: function () {
-      var self = this
-      var xhr = new XMLHttpRequest()
-      xhr.open('GET', apiURL + self.currentBranch)
-      xhr.onload = function () {
-        self.commits = JSON.parse(xhr.responseText)
-        console.log(self.commits[0].html_url)
-      }
-      xhr.send()
-    }
-  }
-})
diff --git a/examples/classic/markdown/index.html b/examples/classic/markdown/index.html
deleted file mode 100644
index bc77e29c07c..00000000000
--- a/examples/classic/markdown/index.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title>Vue.js markdown editor example</title>
-    <link rel="stylesheet" href="style.css">
-    <script src="../../../node_modules/marked/marked.min.js"></script>
-    <script src="../../../node_modules/lodash/lodash.min.js"></script>
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../../dist/vue.min.js"></script>
-  </head>
-  <body>
-
-    <div id="editor">
-      <textarea :value="input" @input="update"></textarea>
-      <div v-html="compiledMarkdown"></div>
-    </div>
-
-    <script>
-      new Vue({
-        el: '#editor',
-        data: {
-          input: '# hello'
-        },
-        computed: {
-          compiledMarkdown: function () {
-            return marked.marked(this.input)
-          }
-        },
-        methods: {
-          update: _.debounce(function (e) {
-            this.input = e.target.value
-          }, 300)
-        }
-      })
-    </script>
-
-  </body>
-</html>
diff --git a/examples/classic/modal/index.html b/examples/classic/modal/index.html
deleted file mode 100644
index 9acb7a42f69..00000000000
--- a/examples/classic/modal/index.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title>Vue.js modal component example</title>
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../../dist/vue.min.js"></script>
-    <link rel="stylesheet" href="style.css">
-  </head>
-  <body>
-    <!-- template for the modal component -->
-    <script type="text/x-template" id="modal-template">
-      <transition name="modal" appear>
-        <div class="modal-mask">
-          <div class="modal-wrapper">
-            <div class="modal-container">
-
-              <div class="modal-header">
-                <slot name="header">
-                  default header
-                </slot>
-              </div>
-
-              <div class="modal-body">
-                <slot name="body">
-                  default body
-                </slot>
-              </div>
-
-              <div class="modal-footer">
-                <slot name="footer">
-                  default footer
-                  <button class="modal-default-button" @click="$emit('close')">
-                    OK
-                  </button>
-                </slot>
-              </div>
-            </div>
-          </div>
-        </div>
-      </transition>
-    </script>
-
-    <!-- app -->
-    <div id="app">
-      <button id="show-modal" @click="showModal = true">Show Modal</button>
-      <!-- use the modal component, pass in the prop -->
-      <modal v-if="showModal" @close="showModal = false">
-        <!--
-          you can use custom content here to overwrite
-          default content
-        -->
-        <h3 slot="header">custom header</h3>
-      </modal>
-    </div>
-
-    <script>
-      // register modal component
-      Vue.component('modal', {
-        template: '#modal-template'
-      })
-
-      // start app
-      new Vue({
-        el: '#app',
-        data: {
-          showModal: false
-        }
-      })
-    </script>
-  </body>
-</html>
diff --git a/examples/classic/move-animations/index.html b/examples/classic/move-animations/index.html
deleted file mode 100644
index e89d137b2b7..00000000000
--- a/examples/classic/move-animations/index.html
+++ /dev/null
@@ -1,93 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <title>Move Animations</title>
-    <style>
-      .container {
-        position: relative;
-        padding: 0;
-      }
-      .item {
-        width: 100%;
-        height: 30px;
-        background-color: #f3f3f3;
-        border: 1px solid #666;
-        box-sizing: border-box;
-      }
-      /* 1. declare transition */
-      .fade-move,
-      .fade-enter-active,
-      .fade-leave-active {
-        transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
-      }
-      /* 2. declare enter from and leave to state */
-      .fade-enter,
-      .fade-leave-to {
-        opacity: 0;
-        transform: scaleY(0.01) translate(30px, 0);
-      }
-      /* 3. ensure leaving items are taken out of layout flow so that moving
-            animations can be calculated correctly. */
-      .fade-leave-active {
-        position: absolute;
-      }
-    </style>
-    <script src="../../../node_modules/lodash/lodash.min.js"></script>
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../../dist/vue.min.js"></script>
-  </head>
-  <body>
-    <div id="el">
-      <button @click="insert">insert at random index</button>
-      <button @click="reset">reset</button>
-      <button @click="shuffle">shuffle</button>
-      <transition-group tag="ul" name="fade" class="container">
-        <item
-          v-for="item in items"
-          class="item"
-          :msg="item"
-          :key="item"
-          @rm="remove(item)"
-        >
-        </item>
-      </transition-group>
-    </div>
-
-    <script>
-      var items = [1, 2, 3, 4, 5]
-      var id = items.length + 1
-
-      var vm = new Vue({
-        el: '#el',
-        data: {
-          items: items
-        },
-        components: {
-          item: {
-            props: ['msg'],
-            template: `<div>{{ msg }} <button @click="$emit('rm')">x</button></div>`
-          }
-        },
-        methods: {
-          insert() {
-            var i = Math.round(Math.random() * this.items.length)
-            this.items.splice(i, 0, id++)
-          },
-          reset() {
-            this.items = [1, 2, 3, 4, 5]
-          },
-          shuffle() {
-            this.items = _.shuffle(this.items)
-          },
-          remove(item) {
-            var i = this.items.indexOf(item)
-            if (i > -1) {
-              this.items.splice(i, 1)
-            }
-          }
-        }
-      })
-    </script>
-  </body>
-</html>
diff --git a/examples/classic/select2/index.html b/examples/classic/select2/index.html
deleted file mode 100644
index e27e0ef80c9..00000000000
--- a/examples/classic/select2/index.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title>Vue.js wrapper component example (jquery plugin: select2)</title>
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../dist/vue.min.js"></script>
-    <script src="https://unpkg.com/jquery"></script>
-    <script src="https://unpkg.com/select2@4.0.3"></script>
-    <link href="https://unpkg.com/select2@4.0.3/dist/css/select2.min.css" rel="stylesheet">
-    <style>
-      html, body {
-        font: 13px/18px sans-serif;
-      }
-      select {
-        min-width: 300px;
-      }
-    </style>
-  </head>
-  <body>
-
-    <div id="el">
-    </div>
-
-    <!-- using string template here to work around HTML <option> placement restriction -->
-    <script type="text/x-template" id="demo-template">
-      <div>
-        <p>Selected: {{ selected }}</p>
-        <select2 :options="options" v-model="selected">
-          <option disabled value="0">Select one</option>
-        </select2>
-      </div>
-    </script>
-
-    <script type="text/x-template" id="select2-template">
-      <select>
-        <slot></slot>
-      </select>
-    </script>
-
-    <script>
-    Vue.component('select2', {
-      props: ['options', 'value'],
-      template: '#select2-template',
-      mounted: function () {
-        var vm = this
-        $(this.$el)
-          .val(this.value)
-          // init select2
-          .select2({ data: this.options })
-          // emit event on change.
-          .on('change', function () {
-            vm.$emit('input', this.value)
-          })
-      },
-      watch: {
-        value: function (value) {
-          // update value
-          $(this.$el).val(value).trigger('change')
-        },
-        options: function (options) {
-          // update options
-          $(this.$el).select2({ data: options })
-        }
-      },
-      destroyed: function () {
-        $(this.$el).off().select2('destroy')
-      }
-    })
-
-    var vm = new Vue({
-      el: '#el',
-      template: '#demo-template',
-      data: {
-        selected: 0,
-        options: [
-          { id: 1, text: 'Hello' },
-          { id: 2, text: 'World' }
-        ]
-      }
-    })
-    </script>
-  </body>
-</html>
diff --git a/examples/classic/todomvc/app.js b/examples/classic/todomvc/app.js
deleted file mode 100644
index 7a1a65486a3..00000000000
--- a/examples/classic/todomvc/app.js
+++ /dev/null
@@ -1,157 +0,0 @@
-// Full spec-compliant TodoMVC with localStorage persistence
-// and hash-based routing in ~150 lines.
-
-// localStorage persistence
-var STORAGE_KEY = 'todos-vuejs-2.0'
-var todoStorage = {
-  fetch: function () {
-    var todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
-    todos.forEach(function (todo, index) {
-      todo.id = index
-    })
-    todoStorage.uid = todos.length
-    return todos
-  },
-  save: function (todos) {
-    localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
-  }
-}
-
-// visibility filters
-var filters = {
-  all: function (todos) {
-    return todos
-  },
-  active: function (todos) {
-    return todos.filter(function (todo) {
-      return !todo.completed
-    })
-  },
-  completed: function (todos) {
-    return todos.filter(function (todo) {
-      return todo.completed
-    })
-  }
-}
-
-// app Vue instance
-var app = new Vue({
-  // app initial state
-  data: {
-    todos: todoStorage.fetch(),
-    newTodo: '',
-    editedTodo: null,
-    visibility: 'all'
-  },
-
-  // watch todos change for localStorage persistence
-  watch: {
-    todos: {
-      handler: function (todos) {
-        todoStorage.save(todos)
-      },
-      deep: true
-    }
-  },
-
-  // computed properties
-  // https://v2.vuejs.org/v2/guide/computed.html
-  computed: {
-    filteredTodos: function () {
-      return filters[this.visibility](this.todos)
-    },
-    remaining: function () {
-      return filters.active(this.todos).length
-    },
-    allDone: {
-      get: function () {
-        return this.remaining === 0
-      },
-      set: function (value) {
-        this.todos.forEach(function (todo) {
-          todo.completed = value
-        })
-      }
-    }
-  },
-
-  filters: {
-    pluralize: function (n) {
-      return n === 1 ? 'item' : 'items'
-    }
-  },
-
-  // methods that implement data logic.
-  // note there's no DOM manipulation here at all.
-  methods: {
-    addTodo: function () {
-      var value = this.newTodo && this.newTodo.trim()
-      if (!value) {
-        return
-      }
-      this.todos.push({
-        id: todoStorage.uid++,
-        title: value,
-        completed: false
-      })
-      this.newTodo = ''
-    },
-
-    removeTodo: function (todo) {
-      this.todos.splice(this.todos.indexOf(todo), 1)
-    },
-
-    editTodo: function (todo) {
-      this.beforeEditCache = todo.title
-      this.editedTodo = todo
-    },
-
-    doneEdit: function (todo) {
-      if (!this.editedTodo) {
-        return
-      }
-      this.editedTodo = null
-      todo.title = todo.title.trim()
-      if (!todo.title) {
-        this.removeTodo(todo)
-      }
-    },
-
-    cancelEdit: function (todo) {
-      this.editedTodo = null
-      todo.title = this.beforeEditCache
-    },
-
-    removeCompleted: function () {
-      this.todos = filters.active(this.todos)
-    }
-  },
-
-  // a custom directive to wait for the DOM to be updated
-  // before focusing on the input field.
-  // https://v2.vuejs.org/v2/guide/custom-directive.html
-  directives: {
-    'todo-focus': function (el, binding) {
-      if (binding.value) {
-        el.focus()
-      }
-    }
-  }
-})
-
-// handle routing
-function onHashChange () {
-  var visibility = window.location.hash.replace(/#\/?/, '')
-  if (filters[visibility]) {
-    app.visibility = visibility
-  } else {
-    window.location.hash = ''
-    app.visibility = 'all'
-  }
-}
-
-window.addEventListener('hashchange', onHashChange)
-onHashChange()
-
-// mount
-app.$mount('.todoapp')
diff --git a/examples/classic/todomvc/index.html b/examples/classic/todomvc/index.html
deleted file mode 100644
index 3d9557edf99..00000000000
--- a/examples/classic/todomvc/index.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<!doctype html>
-<html data-framework="vue">
-  <head>
-    <meta charset="utf-8">
-    <title>Vue.js • TodoMVC</title>
-    <link rel="stylesheet" href="../../../node_modules/todomvc-app-css/index.css">
-    <style>[v-cloak] { display: none; }</style>
-  </head>
-  <body>
-    <section class="todoapp">
-      <header class="header">
-        <h1>todos</h1>
-        <input class="new-todo"
-          autofocus autocomplete="off"
-          placeholder="What needs to be done?"
-          v-model="newTodo"
-          @keyup.enter="addTodo">
-      </header>
-      <section class="main" v-show="todos.length" v-cloak>
-        <input id="toggle-all" class="toggle-all" type="checkbox" v-model="allDone">
-        <label for="toggle-all">Mark all as complete</label>
-        <ul class="todo-list">
-          <li v-for="todo in filteredTodos"
-            class="todo"
-            :key="todo.id"
-            :class="{ completed: todo.completed, editing: todo == editedTodo }">
-            <div class="view">
-              <input class="toggle" type="checkbox" v-model="todo.completed">
-              <label @dblclick="editTodo(todo)">{{ todo.title }}</label>
-              <button class="destroy" @click="removeTodo(todo)"></button>
-            </div>
-            <input class="edit" type="text"
-              v-model="todo.title"
-              v-todo-focus="todo == editedTodo"
-              @blur="doneEdit(todo)"
-              @keyup.enter="doneEdit(todo)"
-              @keyup.esc="cancelEdit(todo)">
-          </li>
-        </ul>
-      </section>
-      <footer class="footer" v-show="todos.length" v-cloak>
-        <span class="todo-count">
-          <strong>{{ remaining }}</strong> {{ remaining | pluralize }} left
-        </span>
-        <ul class="filters">
-          <li><a href="#/all" :class="{ selected: visibility == 'all' }">All</a></li>
-          <li><a href="#/active" :class="{ selected: visibility == 'active' }">Active</a></li>
-          <li><a href="#/completed" :class="{ selected: visibility == 'completed' }">Completed</a></li>
-        </ul>
-        <button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">
-          Clear completed
-        </button>
-      </footer>
-    </section>
-    <footer class="info">
-      <p>Double-click to edit a todo</p>
-      <p>Written by <a href="https://evanyou.me">Evan You</a></p>
-      <p>Part of <a href="https://todomvc.com">TodoMVC</a></p>
-    </footer>
-
-    <script>
-    // for testing
-    if (navigator.userAgent.indexOf('PhantomJS') > -1) localStorage.clear()
-    </script>
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../../dist/vue.min.js"></script>
-    <script src="app.js"></script>
-  </body>
-</html>
diff --git a/examples/commits/app.js b/examples/commits/app.js
new file mode 100644
index 00000000000..84e26828510
--- /dev/null
+++ b/examples/commits/app.js
@@ -0,0 +1,66 @@
+var apiURL = 'https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha='
+var isPhantom = navigator.userAgent.indexOf('PhantomJS') > -1
+
+/**
+ * Test mocks
+ */
+
+var mocks = {
+  master: [{sha:'111111111111', commit: {message:'one', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}},{sha:'111111111111', commit: {message:'hi', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}},{sha:'111111111111', commit: {message:'hi', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}}],
+  dev: [{sha:'222222222222', commit: {message:'two', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}},{sha:'111111111111', commit: {message:'hi', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}},{sha:'111111111111', commit: {message:'hi', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}}]
+}
+
+function mockData () {
+  this.commits = mocks[this.currentBranch]
+}
+
+/**
+ * Actual demo
+ */
+
+var demo = new Vue({
+
+  el: '#demo',
+
+  data: {
+    branches: ['master', 'dev'],
+    currentBranch: 'master',
+    commits: null
+  },
+
+  created: function () {
+    this.fetchData()
+  },
+
+  watch: {
+    currentBranch: 'fetchData'
+  },
+
+  filters: {
+    truncate: function (v) {
+      var newline = v.indexOf('\n')
+      return newline > 0 ? v.slice(0, newline) : v
+    },
+    formatDate: function (v) {
+      return v.replace(/T|Z/g, ' ')
+    }
+  },
+
+  methods: {
+    fetchData: function () {
+      // CasperJS fails at cross-domain XHR even with
+      // --web-security=no, have to mock data here.
+      if (isPhantom) {
+        return mockData.call(this)
+      }
+      var xhr = new XMLHttpRequest()
+      var self = this
+      xhr.open('GET', apiURL + self.currentBranch)
+      xhr.onload = function () {
+        self.commits = JSON.parse(xhr.responseText)
+        console.log(self.commits[0].html_url)
+      }
+      xhr.send()
+    }
+  }
+})
diff --git a/examples/classic/commits/index.html b/examples/commits/index.html
similarity index 57%
rename from examples/classic/commits/index.html
rename to examples/commits/index.html
index 10bc8d4d40a..022fca73b27 100644
--- a/examples/classic/commits/index.html
+++ b/examples/commits/index.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html>
   <head>
-    <title>Vue.js github commits example</title>
+    <title>Vue.js commits example</title>
     <style>
       #demo {
         font-family: 'Helvetica', Arial, sans-serif;
@@ -18,27 +18,26 @@
         font-weight: bold;
       }
     </style>
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../../dist/vue.min.js"></script>
+    <script src="../../dist/vue.js"></script>
   </head>
   <body>
     <div id="demo">
       <h1>Latest Vue.js Commits</h1>
       <template v-for="branch in branches">
         <input type="radio"
+          name="branch"
           :id="branch"
           :value="branch"
-          name="branch"
           v-model="currentBranch">
-        <label :for="branch">{{ branch }}</label>
+        <label :for="branch">{{branch}}</label>
       </template>
-      <p>vuejs/vue@{{ currentBranch }}</p>
+      <p>vuejs/vue@{{currentBranch}}</p>
       <ul>
         <li v-for="record in commits">
-          <a :href="record.html_url" target="_blank" class="commit">{{ record.sha.slice(0, 7) }}</a>
-          - <span class="message">{{ record.commit.message | truncate }}</span><br>
-          by <span class="author"><a :href="record.author.html_url" target="_blank">{{ record.commit.author.name }}</a></span>
-          at <span class="date">{{ record.commit.author.date | formatDate }}</span>
+          <a :href="record.html_url" target="_blank" class="commit">{{record.sha.slice(0, 7)}}</a>
+          - <span class="message">{{record.commit.message | truncate}}</span><br>
+          by <span class="author">{{record.commit.author.name}}</span>
+          at <span class="date">{{record.commit.author.date | formatDate}}</span>
         </li>
       </ul>
     </div>
diff --git a/examples/composition/commits.html b/examples/composition/commits.html
deleted file mode 100644
index 3c16a3f7bb8..00000000000
--- a/examples/composition/commits.html
+++ /dev/null
@@ -1,75 +0,0 @@
-<script src="../../dist/vue.min.js"></script>
-
-<div id="demo">
-  <h1>Latest Vue.js Commits</h1>
-  <template v-for="branch in branches">
-    <input type="radio"
-      :id="branch"
-      :value="branch"
-      name="branch"
-      v-model="currentBranch">
-    <label :for="branch">{{ branch }}</label>
-  </template>
-  <p>vuejs/vue@{{ currentBranch }}</p>
-  <ul>
-    <li v-for="{ html_url, sha, author, commit } in commits">
-      <a :href="html_url" target="_blank" class="commit">{{ sha.slice(0, 7) }}</a>
-      - <span class="message">{{ truncate(commit.message) }}</span><br>
-      by <span class="author"><a :href="author.html_url" target="_blank">{{ commit.author.name }}</a></span>
-      at <span class="date">{{ formatDate(commit.author.date) }}</span>
-    </li>
-  </ul>
-</div>
-
-<script>
-const { ref, watchEffect } = Vue
-const API_URL = `https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha=`
-
-const truncate = v => {
-  const newline = v.indexOf('\n')
-  return newline > 0 ? v.slice(0, newline) : v
-}
-
-const formatDate = v => v.replace(/T|Z/g, ' ')
-
-new Vue({
-  setup() {
-    const currentBranch = ref('main')
-    const commits = ref(null)
-
-    watchEffect(() => {
-      fetch(`${API_URL}${currentBranch.value}`)
-        .then(res => res.json())
-        .then(data => {
-          console.log(data)
-          commits.value = data
-        })
-    })
-
-    return {
-      branches: ['main', 'dev'],
-      currentBranch,
-      commits,
-      truncate,
-      formatDate
-    }
-  }
-}).$mount('#demo')
-</script>
-
-<style>
-  #demo {
-    font-family: 'Helvetica', Arial, sans-serif;
-  }
-  a {
-    text-decoration: none;
-    color: #f66;
-  }
-  li {
-    line-height: 1.5em;
-    margin-bottom: 20px;
-  }
-  .author, .date {
-    font-weight: bold;
-  }
-</style>
diff --git a/examples/composition/grid.html b/examples/composition/grid.html
deleted file mode 100644
index 090d3d7dbad..00000000000
--- a/examples/composition/grid.html
+++ /dev/null
@@ -1,173 +0,0 @@
-<script src="../../dist/vue.min.js"></script>
-
-<!-- DemoGrid component template -->
-<script type="text/x-template" id="grid-template">
-  <table v-if="filteredData.length">
-    <thead>
-      <tr>
-        <th v-for="key in columns"
-          @click="sortBy(key)"
-          :class="{ active: state.sortKey == key }">
-          {{ capitalize(key) }}
-          <span class="arrow" :class="state.sortOrders[key] > 0 ? 'asc' : 'dsc'">
-          </span>
-        </th>
-      </tr>
-    </thead>
-    <tbody>
-      <tr v-for="entry in filteredData">
-        <td v-for="key in columns">
-          {{entry[key]}}
-        </td>
-      </tr>
-    </tbody>
-  </table>
-  <p v-else>No matches found.</p>
-</script>
-<!-- DemoGrid component script -->
-<script>
-const { reactive, computed } = Vue
-
-const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
-
-const DemoGrid = {
-  template: '#grid-template',
-  props: {
-    data: Array,
-    columns: Array,
-    filterKey: String
-  },
-  setup(props) {
-    const state = reactive({
-      sortKey: '',
-      sortOrders: props.columns.reduce((o, key) => (o[key] = 1, o), {})
-    })
-
-    const filteredData = computed(() => {
-      let { data, filterKey } = props
-      if (filterKey) {
-        filterKey = filterKey.toLowerCase()
-        data = data.filter(row => {
-          return Object.keys(row).some(key => {
-            return String(row[key]).toLowerCase().indexOf(filterKey) > -1
-          })
-        })
-      }
-      const { sortKey } = state
-      if (sortKey) {
-        const order = state.sortOrders[sortKey]
-        data = data.slice().sort((a, b) => {
-          a = a[sortKey]
-          b = b[sortKey]
-          return (a === b ? 0 : a > b ? 1 : -1) * order
-        })
-      }
-      return data
-    })
-
-    function sortBy(key) {
-      state.sortKey = key
-      state.sortOrders[key] *= -1
-    }
-
-    return {
-      state,
-      filteredData,
-      sortBy,
-      capitalize
-    }
-  }
-}
-</script>
-
-<!-- App template (in DOM) -->
-<div id="demo">
-  <form id="search">
-    Search <input name="query" v-model="searchQuery">
-  </form>
-  <demo-grid
-    :data="gridData"
-    :columns="gridColumns"
-    :filter-key="searchQuery">
-  </demo-grid>
-</div>
-<!-- App script -->
-<script>
-new Vue({
-  components: {
-    DemoGrid
-  },
-  data: () => ({
-    searchQuery: '',
-    gridColumns: ['name', 'power'],
-    gridData: [
-      { name: 'Chuck Norris', power: Infinity },
-      { name: 'Bruce Lee', power: 9000 },
-      { name: 'Jackie Chan', power: 7000 },
-      { name: 'Jet Li', power: 8000 }
-    ]
-  })
-}).$mount('#demo')
-</script>
-
-<style>
-body {
-  font-family: Helvetica Neue, Arial, sans-serif;
-  font-size: 14px;
-  color: #444;
-}
-
-table {
-  border: 2px solid #42b983;
-  border-radius: 3px;
-  background-color: #fff;
-}
-
-th {
-  background-color: #42b983;
-  color: rgba(255,255,255,0.66);
-  cursor: pointer;
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  -ms-user-select: none;
-  user-select: none;
-}
-
-td {
-  background-color: #f9f9f9;
-}
-
-th, td {
-  min-width: 120px;
-  padding: 10px 20px;
-}
-
-th.active {
-  color: #fff;
-}
-
-th.active .arrow {
-  opacity: 1;
-}
-
-.arrow {
-  display: inline-block;
-  vertical-align: middle;
-  width: 0;
-  height: 0;
-  margin-left: 5px;
-  opacity: 0.66;
-}
-
-.arrow.asc {
-  border-left: 4px solid transparent;
-  border-right: 4px solid transparent;
-  border-bottom: 4px solid #fff;
-}
-
-.arrow.dsc {
-  border-left: 4px solid transparent;
-  border-right: 4px solid transparent;
-  border-top: 4px solid #fff;
-}
-</style>
diff --git a/examples/composition/markdown.html b/examples/composition/markdown.html
deleted file mode 100644
index d3387de4a43..00000000000
--- a/examples/composition/markdown.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<script src="../../node_modules/marked/marked.min.js"></script>
-<script src="../../node_modules/lodash/lodash.min.js"></script>
-<script src="../../dist/vue.min.js"></script>
-
-<div id="editor">
-  <textarea :value="input" @input="update"></textarea>
-  <div v-html="output"></div>
-</div>
-
-<script>
-  const { ref, computed } = Vue
-
-  new Vue({
-    setup() {
-      const input = ref('# hello')
-      const output = computed(() => marked.marked(input.value))
-      const update = _.debounce(e => {
-        input.value = e.target.value
-      }, 300)
-
-      return {
-        input,
-        output,
-        update
-      }
-    }
-  }).$mount('#editor')
-</script>
-
-<style>
-  html,
-  body,
-  #editor {
-    margin: 0;
-    height: 100%;
-    font-family: 'Helvetica Neue', Arial, sans-serif;
-    color: #333;
-  }
-
-  textarea,
-  #editor div {
-    display: inline-block;
-    width: 49%;
-    height: 100%;
-    vertical-align: top;
-    -webkit-box-sizing: border-box;
-    -moz-box-sizing: border-box;
-    box-sizing: border-box;
-    padding: 0 20px;
-  }
-
-  textarea {
-    border: none;
-    border-right: 1px solid #ccc;
-    resize: none;
-    outline: none;
-    background-color: #f6f6f6;
-    font-size: 14px;
-    font-family: 'Monaco', courier, monospace;
-    padding: 20px;
-  }
-
-  code {
-    color: #f66;
-  }
-</style>
diff --git a/examples/composition/svg.html b/examples/composition/svg.html
deleted file mode 100644
index 2b4d5e0c316..00000000000
--- a/examples/composition/svg.html
+++ /dev/null
@@ -1,172 +0,0 @@
-<script src="../../dist/vue.min.js"></script>
-<script>
-  const { ref, computed, createApp } = Vue
-
-  // math helper...
-  function valueToPoint(value, index, total) {
-    var x = 0
-    var y = -value * 0.8
-    var angle = ((Math.PI * 2) / total) * index
-    var cos = Math.cos(angle)
-    var sin = Math.sin(angle)
-    var tx = x * cos - y * sin + 100
-    var ty = x * sin + y * cos + 100
-    return {
-      x: tx,
-      y: ty
-    }
-  }
-
-  const AxisLabel = {
-    template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
-    props: {
-      stat: Object,
-      index: Number,
-      total: Number
-    },
-    setup(props) {
-      return {
-        point: computed(() =>
-          valueToPoint(+props.stat.value + 10, props.index, props.total)
-        )
-      }
-    }
-  }
-</script>
-
-<!-- template for the polygraph component. -->
-<script type="text/x-template" id="polygraph-template">
-  <g>
-    <polygon :points="points"></polygon>
-    <circle cx="100" cy="100" r="80"></circle>
-    <axis-label
-      v-for="(stat, index) in stats"
-      :stat="stat"
-      :index="index"
-      :total="stats.length">
-    </axis-label>
-  </g>
-</script>
-
-<script>
-  const Polygraph = {
-    props: ['stats'],
-    template: '#polygraph-template',
-    setup(props) {
-      return {
-        points: computed(() => {
-          const total = props.stats.length
-          return props.stats
-            .map((stat, i) => {
-              const point = valueToPoint(stat.value, i, total)
-              return point.x + ',' + point.y
-            })
-            .join(' ')
-        })
-      }
-    },
-    components: {
-      AxisLabel
-    }
-  }
-</script>
-
-<!-- demo root element -->
-<div id="demo">
-  <!-- Use the polygraph component -->
-  <svg width="200" height="200">
-    <polygraph :stats="stats"></polygraph>
-  </svg>
-  <!-- controls -->
-  <div v-for="stat in stats">
-    <label>{{stat.label}}</label>
-    <input type="range" v-model="stat.value" min="0" max="100" />
-    <span>{{stat.value}}</span>
-    <button @click="remove(stat)" class="remove">X</button>
-  </div>
-  <form id="add">
-    <input name="newlabel" v-model="newLabel" />
-    <button @click="add">Add a Stat</button>
-  </form>
-  <pre id="raw">{{ stats }}</pre>
-</div>
-
-<script>
-  const globalStats = [
-    { label: 'A', value: 100 },
-    { label: 'B', value: 100 },
-    { label: 'C', value: 100 },
-    { label: 'D', value: 100 },
-    { label: 'E', value: 100 },
-    { label: 'F', value: 100 }
-  ]
-
-  new Vue({
-    components: {
-      Polygraph
-    },
-    setup() {
-      const newLabel = ref('')
-      const stats = ref(globalStats)
-
-      function add(e) {
-        e.preventDefault()
-        if (!newLabel.value) return
-        stats.value.push({
-          label: newLabel.value,
-          value: 100
-        })
-        newLabel.value = ''
-      }
-
-      function remove(stat) {
-        if (stats.value.length > 3) {
-          stats.value.splice(stats.value.indexOf(stat), 1)
-        } else {
-          alert("Can't delete more!")
-        }
-      }
-
-      return {
-        newLabel,
-        stats,
-        add,
-        remove
-      }
-    }
-  }).$mount('#demo')
-</script>
-
-<style>
-  body {
-    font-family: Helvetica Neue, Arial, sans-serif;
-  }
-
-  polygon {
-    fill: #42b983;
-    opacity: 0.75;
-  }
-
-  circle {
-    fill: transparent;
-    stroke: #999;
-  }
-
-  text {
-    font-family: Helvetica Neue, Arial, sans-serif;
-    font-size: 10px;
-    fill: #666;
-  }
-
-  label {
-    display: inline-block;
-    margin-left: 10px;
-    width: 20px;
-  }
-
-  #raw {
-    position: absolute;
-    top: 0;
-    left: 300px;
-  }
-</style>
diff --git a/examples/composition/todomvc.html b/examples/composition/todomvc.html
deleted file mode 100644
index d95b39d2735..00000000000
--- a/examples/composition/todomvc.html
+++ /dev/null
@@ -1,241 +0,0 @@
-<script src="../../dist/vue.min.js"></script>
-<link
-  rel="stylesheet"
-  href="../../node_modules/todomvc-app-css/index.css"
-/>
-
-<div id="app">
-  <section class="todoapp">
-    <header class="header">
-      <h1>todos</h1>
-      <input
-        class="new-todo"
-        autofocus
-        autocomplete="off"
-        placeholder="What needs to be done?"
-        v-model="state.newTodo"
-        @keyup.enter="addTodo"
-      />
-    </header>
-    <section class="main" v-show="state.todos.length">
-      <input
-        id="toggle-all"
-        class="toggle-all"
-        type="checkbox"
-        v-model="state.allDone"
-      />
-      <label for="toggle-all">Mark all as complete</label>
-      <ul class="todo-list">
-        <li
-          v-for="todo in state.filteredTodos"
-          class="todo"
-          :key="todo.id"
-          :class="{ completed: todo.completed, editing: todo === state.editedTodo }"
-        >
-          <div class="view">
-            <input class="toggle" type="checkbox" v-model="todo.completed" />
-            <label @dblclick="editTodo(todo)">{{ todo.title }}</label>
-            <button class="destroy" @click="removeTodo(todo)"></button>
-          </div>
-          <input
-            class="edit"
-            type="text"
-            v-model="todo.title"
-            v-todo-focus="todo === state.editedTodo"
-            @blur="doneEdit(todo)"
-            @keyup.enter="doneEdit(todo)"
-            @keyup.escape="cancelEdit(todo)"
-          />
-        </li>
-      </ul>
-    </section>
-    <footer class="footer" v-show="state.todos.length">
-      <span class="todo-count">
-        <strong>{{ state.remaining }}</strong>
-        <span>{{ state.remainingText }}</span>
-      </span>
-      <ul class="filters">
-        <li>
-          <a href="#/all" :class="{ selected: state.visibility === 'all' }"
-            >All</a
-          >
-        </li>
-        <li>
-          <a
-            href="#/active"
-            :class="{ selected: state.visibility === 'active' }"
-            >Active</a
-          >
-        </li>
-        <li>
-          <a
-            href="#/completed"
-            :class="{ selected: state.visibility === 'completed' }"
-            >Completed</a
-          >
-        </li>
-      </ul>
-
-      <button
-        class="clear-completed"
-        @click="removeCompleted"
-        v-show="state.todos.length > state.remaining"
-      >
-        Clear completed
-      </button>
-    </footer>
-  </section>
-</div>
-
-<script>
-  const { reactive, computed, watchEffect, onMounted, onUnmounted } = Vue
-
-  const STORAGE_KEY = 'todos-vuejs-3.x-composition'
-  const todoStorage = {
-    fetch() {
-      const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
-      todos.forEach((todo, index) => {
-        todo.id = index
-      })
-      todoStorage.uid = todos.length
-      return todos
-    },
-    save(todos) {
-      localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
-    }
-  }
-
-  const filters = {
-    all(todos) {
-      return todos
-    },
-    active(todos) {
-      return todos.filter(todo => {
-        return !todo.completed
-      })
-    },
-    completed(todos) {
-      return todos.filter(function (todo) {
-        return todo.completed
-      })
-    }
-  }
-
-  function pluralize(n) {
-    return n === 1 ? 'item' : 'items'
-  }
-
-  new Vue({
-    setup() {
-      const state = reactive({
-        todos: todoStorage.fetch(),
-        editedTodo: null,
-        newTodo: '',
-        beforeEditCache: '',
-        visibility: 'all',
-        remaining: computed(() => {
-          return filters.active(state.todos).length
-        }),
-        remainingText: computed(() => {
-          return ` ${pluralize(state.remaining)} left`
-        }),
-        filteredTodos: computed(() => {
-          return filters[state.visibility](state.todos)
-        }),
-        allDone: computed({
-          get: function () {
-            return state.remaining === 0
-          },
-          set: function (value) {
-            state.todos.forEach(todo => {
-              todo.completed = value
-            })
-          }
-        })
-      })
-
-      watchEffect(() => {
-        todoStorage.save(state.todos)
-      })
-
-      onMounted(() => {
-        window.addEventListener('hashchange', onHashChange)
-        onHashChange()
-      })
-
-      onUnmounted(() => {
-        window.removeEventListener('hashchange', onHashChange)
-      })
-
-      function onHashChange() {
-        const visibility = window.location.hash.replace(/#\/?/, '')
-        if (filters[visibility]) {
-          state.visibility = visibility
-        } else {
-          window.location.hash = ''
-          state.visibility = 'all'
-        }
-      }
-
-      function addTodo() {
-        const value = state.newTodo && state.newTodo.trim()
-        if (!value) {
-          return
-        }
-        state.todos.push({
-          id: todoStorage.uid++,
-          title: value,
-          completed: false
-        })
-        state.newTodo = ''
-      }
-
-      function removeTodo(todo) {
-        state.todos.splice(state.todos.indexOf(todo), 1)
-      }
-
-      function editTodo(todo) {
-        state.beforeEditCache = todo.title
-        state.editedTodo = todo
-      }
-
-      function doneEdit(todo) {
-        if (!state.editedTodo) {
-          return
-        }
-        state.editedTodo = null
-        todo.title = todo.title.trim()
-        if (!todo.title) {
-          removeTodo(todo)
-        }
-      }
-
-      function cancelEdit(todo) {
-        state.editedTodo = null
-        todo.title = state.beforeEditCache
-      }
-
-      function removeCompleted() {
-        state.todos = filters.active(state.todos)
-      }
-
-      return {
-        state,
-        addTodo,
-        removeTodo,
-        editTodo,
-        doneEdit,
-        cancelEdit,
-        removeCompleted
-      }
-    },
-
-    directives: {
-      'todo-focus': (el, { value }) => {
-        if (value) {
-          el.focus()
-        }
-      }
-    }
-  }).$mount('#app')
-</script>
diff --git a/examples/composition/tree.html b/examples/composition/tree.html
deleted file mode 100644
index c39fe3987b7..00000000000
--- a/examples/composition/tree.html
+++ /dev/null
@@ -1,124 +0,0 @@
-<script src="../../dist/vue.min.js"></script>
-
-<!-- item template -->
-<script type="text/x-template" id="item-template">
-  <li>
-    <div
-      :class="{bold: isFolder}"
-      @click="toggle"
-      @dblclick="changeType">
-      {{model.name}}
-      <span v-if="isFolder">[{{open ? '-' : '+'}}]</span>
-    </div>
-    <ul v-if="isFolder" v-show="open">
-      <tree-item
-        class="item"
-        v-for="model in model.children"
-        :model="model">
-      </tree-item>
-      <li class="add" @click="addChild">+</li>
-    </ul>
-  </li>
-</script>
-<!-- item script -->
-<script>
-  const { reactive, computed, toRefs } = Vue
-
-  const TreeItem = {
-    name: 'TreeItem', // necessary for self-reference
-    template: '#item-template',
-    props: {
-      model: Object
-    },
-    setup(props) {
-      const state = reactive({
-        open: false,
-        isFolder: computed(() => {
-          return props.model.children && props.model.children.length
-        })
-      })
-
-      function toggle() {
-        state.open = !state.open
-      }
-
-      function changeType() {
-        if (!state.isFolder) {
-          Vue.set(props.model, 'children', [])
-          addChild()
-          state.open = true
-        }
-      }
-
-      function addChild() {
-        props.model.children.push({ name: 'new stuff' })
-      }
-
-      return {
-        ...toRefs(state),
-        toggle,
-        changeType,
-        addChild
-      }
-    }
-  }
-</script>
-
-<p>(You can double click on an item to turn it into a folder.)</p>
-
-<!-- the app root element -->
-<ul id="demo">
-  <tree-item class="item" :model="treeData"></tree-item>
-</ul>
-
-<script>
-  const treeData = {
-    name: 'My Tree',
-    children: [
-      { name: 'hello' },
-      { name: 'wat' },
-      {
-        name: 'child folder',
-        children: [
-          {
-            name: 'child folder',
-            children: [{ name: 'hello' }, { name: 'wat' }]
-          },
-          { name: 'hello' },
-          { name: 'wat' },
-          {
-            name: 'child folder',
-            children: [{ name: 'hello' }, { name: 'wat' }]
-          }
-        ]
-      }
-    ]
-  }
-
-  new Vue({
-    components: {
-      TreeItem
-    },
-    data: () => ({
-      treeData
-    })
-  }).$mount('#demo')
-</script>
-
-<style>
-  body {
-    font-family: Menlo, Consolas, monospace;
-    color: #444;
-  }
-  .item {
-    cursor: pointer;
-  }
-  .bold {
-    font-weight: bold;
-  }
-  ul {
-    padding-left: 1em;
-    line-height: 1.5em;
-    list-style-type: dot;
-  }
-</style>
diff --git a/examples/classic/elastic-header/index.html b/examples/elastic-header/index.html
similarity index 71%
rename from examples/classic/elastic-header/index.html
rename to examples/elastic-header/index.html
index 095c1beeb20..57f132626de 100644
--- a/examples/classic/elastic-header/index.html
+++ b/examples/elastic-header/index.html
@@ -3,13 +3,12 @@
   <head>
     <meta charset="utf-8">
     <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
-    <title>Vue.js elastic header example</title>
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../../dist/vue.min.js"></script>
+    <title></title>
+    <script src="../../dist/vue.js"></script>
     <script src="http://dynamicsjs.com/lib/dynamics.js"></script>
     <link rel="stylesheet" href="style.css">
     <!-- template for the component -->
-    <script type="text/x-template" id="header-view-template">
+    <script type="x/template" id="header-view-template">
       <div class="draggable-header-view"
         @mousedown="startDrag" @touchstart="startDrag"
         @mousemove="onDrag" @touchmove="onDrag"
@@ -26,19 +25,17 @@
       </div>
     </script>
   </head>
-  <body>
+  <body @touchmove.prevent>
 
-    <div id="app" @touchmove.prevent>
-      <draggable-header-view>
-        <template slot="header">
-          <h1>Elastic Draggable SVG Header</h1>
-          <p>with <a href="https://v2.vuejs.org">Vue.js</a> + <a href="http://dynamicsjs.com">dynamics.js</a></p>
-        </template>
-        <template slot="content">
-          <p>Note this is just an effect demo - there are of course many additional details if you want to use this in production, e.g. handling responsive sizes, reload threshold and content scrolling. Those are out of scope for this quick little hack. However, the idea is that you can hide them as internal details of a Vue.js component and expose a simple Web-Component-like interface.</p>
-        </template>
-      </draggable-header-view>
-    </div>
+    <draggable-header-view>
+      <template slot="header">
+        <h1>Elastic Draggable SVG Header</h1>
+        <p>with <a href="http://vuejs.org">Vue.js</a> + <a href="http://dynamicsjs.com">dynamics.js</a></p>
+      </template>
+      <template slot="content">
+        <p>Note this is just an effect demo - there are of course many additional details if you want to use this in production, e.g. handling responsive sizes, reload threshold and content scrolling. Those are out of scope for this quick little hack. However, the idea is that you can hide them as internal details of a Vue.js component and expose a simple Web-Component-like interface.</p>
+      </template>
+    </draggable-header-view>
 
     <script>
     Vue.component('draggable-header-view', {
@@ -99,7 +96,7 @@ <h1>Elastic Draggable SVG Header</h1>
       }
     })
 
-    new Vue({ el: '#app' })
+    new Vue({ el: 'body' })
     </script>
   </body>
 </html>
diff --git a/examples/classic/elastic-header/style.css b/examples/elastic-header/style.css
similarity index 100%
rename from examples/classic/elastic-header/style.css
rename to examples/elastic-header/style.css
diff --git a/examples/classic/firebase/app.js b/examples/firebase/app.js
similarity index 80%
rename from examples/classic/firebase/app.js
rename to examples/firebase/app.js
index be329031e86..50c86754630 100644
--- a/examples/classic/firebase/app.js
+++ b/examples/firebase/app.js
@@ -1,14 +1,7 @@
 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,}))$/
 
-// Setup Firebase
-var config = {
-  apiKey: "AIzaSyAi_yuJciPXLFr_PYPeU3eTvtXf8jbJ8zw",
-  authDomain: "vue-demo-537e6.firebaseapp.com",
-  databaseURL: "https://vue-demo-537e6.firebaseio.com"
-}
-firebase.initializeApp(config)
-
-var usersRef = firebase.database().ref('users')
+// Firebase ref
+var usersRef = new Firebase('https://vue-demo.firebaseIO.com/users')
 
 // create Vue app
 var app = new Vue({
diff --git a/examples/classic/firebase/index.html b/examples/firebase/index.html
similarity index 58%
rename from examples/classic/firebase/index.html
rename to examples/firebase/index.html
index f2d2f805f07..0f057f39242 100644
--- a/examples/classic/firebase/index.html
+++ b/examples/firebase/index.html
@@ -1,28 +1,27 @@
 <!DOCTYPE html>
 <html lang="en">
   <head>
-    <title>Vue.js firebase + validation example</title>
+    <title>Vue.js Firebase example</title>
     <meta charset="utf-8">
     <link rel="stylesheet" type="text/css" href="style.css">
     <!-- Vue -->
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../../dist/vue.min.js"></script>
+    <script src="../../dist/vue.js"></script>
     <!-- Firebase -->
-    <script src="https://www.gstatic.com/firebasejs/3.4.0/firebase.js"></script>
+    <script src="https://cdn.firebase.com/js/client/2.4.2/firebase.js"></script>
     <!-- VueFire -->
-    <script src="https://unpkg.com/vuefire@1.3.0"></script>
+    <script src="https://cdn.jsdelivr.net/vuefire/1.0.1/vuefire.min.js"></script>
   </head>
   <body>
     <div id="app">
-      <ul is="transition-group">
-        <li v-for="user in users" class="user" :key="user['.key']">
+      <ul>
+        <li class="user" v-for="user in users" transition>
           <span>{{user.name}} - {{user.email}}</span>
           <button v-on:click="removeUser(user)">X</button>
         </li>
       </ul>
       <form id="form" v-on:submit.prevent="addUser">
-        <input v-model="newUser.name" placeholder="Add Name">
-        <input v-model="newUser.email" placeholder="Add Email">
+        <input v-model="newUser.name">
+        <input v-model="newUser.email">
         <input type="submit" value="Add User">
       </form>
       <ul class="errors">
diff --git a/examples/classic/firebase/style.css b/examples/firebase/style.css
similarity index 94%
rename from examples/classic/firebase/style.css
rename to examples/firebase/style.css
index 369ab1c7dc2..bfaaa40a89d 100644
--- a/examples/classic/firebase/style.css
+++ b/examples/firebase/style.css
@@ -19,7 +19,7 @@ ul {
   border-bottom: 1px solid #eee;
 }
 
-.v-enter, .v-leave-to {
+.v-enter, .v-leave {
   height: 0;
   padding-top: 0;
   padding-bottom: 0;
diff --git a/examples/classic/grid/grid.js b/examples/grid/grid.js
similarity index 51%
rename from examples/classic/grid/grid.js
rename to examples/grid/grid.js
index 8566f1ebc1c..c2480602824 100644
--- a/examples/classic/grid/grid.js
+++ b/examples/grid/grid.js
@@ -17,34 +17,6 @@ Vue.component('demo-grid', {
       sortOrders: sortOrders
     }
   },
-  computed: {
-    filteredData: function () {
-      var sortKey = this.sortKey
-      var filterKey = this.filterKey && this.filterKey.toLowerCase()
-      var order = this.sortOrders[sortKey] || 1
-      var data = this.data
-      if (filterKey) {
-        data = data.filter(function (row) {
-          return Object.keys(row).some(function (key) {
-            return String(row[key]).toLowerCase().indexOf(filterKey) > -1
-          })
-        })
-      }
-      if (sortKey) {
-        data = data.slice().sort(function (a, b) {
-          a = a[sortKey]
-          b = b[sortKey]
-          return (a === b ? 0 : a > b ? 1 : -1) * order
-        })
-      }
-      return data
-    }
-  },
-  filters: {
-    capitalize: function (str) {
-      return str.charAt(0).toUpperCase() + str.slice(1)
-    }
-  },
   methods: {
     sortBy: function (key) {
       this.sortKey = key
diff --git a/examples/classic/grid/index.html b/examples/grid/index.html
similarity index 70%
rename from examples/classic/grid/index.html
rename to examples/grid/index.html
index 41b07060afe..b3c69315373 100644
--- a/examples/classic/grid/index.html
+++ b/examples/grid/index.html
@@ -4,34 +4,36 @@
     <meta charset="utf-8">
     <title>Vue.js grid component example</title>
     <link rel="stylesheet" href="style.css">
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../../dist/vue.min.js"></script>
+    <script src="../../dist/vue.js"></script>
     </head>
   <body>
 
     <!-- component template -->
     <script type="text/x-template" id="grid-template">
-      <table v-if="filteredData.length">
+      <table>
         <thead>
           <tr>
             <th v-for="key in columns"
               @click="sortBy(key)"
-              :class="{ active: sortKey == key }">
-              {{ key | capitalize }}
-              <span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
+              :class="{active: sortKey == key}">
+              {{key | capitalize}}
+              <span class="arrow"
+                :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
               </span>
             </th>
           </tr>
         </thead>
         <tbody>
-          <tr v-for="entry in filteredData">
+          <tr v-for="
+            entry in data
+            | filterBy filterKey
+            | orderBy sortKey sortOrders[sortKey]">
             <td v-for="key in columns">
               {{entry[key]}}
             </td>
           </tr>
         </tbody>
       </table>
-      <p v-else>No matches found.</p>
     </script>
 
     <!-- demo root element -->
diff --git a/examples/classic/grid/style.css b/examples/grid/style.css
similarity index 94%
rename from examples/classic/grid/style.css
rename to examples/grid/style.css
index 2221ea47e86..efc955fe9c5 100644
--- a/examples/classic/grid/style.css
+++ b/examples/grid/style.css
@@ -16,8 +16,7 @@ th {
   cursor: pointer;
   -webkit-user-select: none;
   -moz-user-select: none;
-  -ms-user-select: none;
-  user-select: none;
+  -user-select: none;
 }
 
 td {
@@ -56,4 +55,4 @@ th.active .arrow {
   border-left: 4px solid transparent;
   border-right: 4px solid transparent;
   border-top: 4px solid #fff;
-}
+}
\ No newline at end of file
diff --git a/examples/markdown/index.html b/examples/markdown/index.html
new file mode 100644
index 00000000000..f6d783aa568
--- /dev/null
+++ b/examples/markdown/index.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Vue.js markdown editor example</title>
+    <link rel="stylesheet" href="style.css">
+    <script src="../../dist/vue.js"></script>
+    <script src="marked.min.js"></script>
+  </head>
+  <body>
+
+    <div id="editor">
+      <textarea v-model="input" debounce="300"></textarea>
+      <div v-html="input | marked"></div>
+    </div>
+
+    <script>
+      new Vue({
+        el: '#editor',
+        data: {
+          input: '# hello'
+        },
+        filters: {
+          marked: marked
+        }
+      })
+    </script>
+
+  </body>
+</html>
\ No newline at end of file
diff --git a/examples/markdown/marked.min.js b/examples/markdown/marked.min.js
new file mode 100644
index 00000000000..45adb9e491f
--- /dev/null
+++ b/examples/markdown/marked.min.js
@@ -0,0 +1,6 @@
+/**
+ * marked - a markdown parser
+ * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
+ * https://github.com/chjj/marked
+ */
+(function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,blockquote:/^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr","\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))")("def","\\n+(?="+block.def.source+")")();block.blockquote=replace(block.blockquote)("def",block.def)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b";block.html=replace(block.html)("comment",/<!--[\s\S]*?-->/)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,paragraph:/^/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1","\\2")+"|"+block.list.source.replace("\\1","\\3")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm){if(this.options.tables){this.rules=block.tables}else{this.rules=block.gfm}}}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g,"    ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top,bq){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1){this.tokens.push({type:"space"})}}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].split(/ *\| */)}this.tokens.push(item);continue}if(cap=this.rules.lheading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[2]==="="?1:2,text:cap[1]});continue}if(cap=this.rules.hr.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"hr"});continue}if(cap=this.rules.blockquote.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"blockquote_start"});cap=cap[0].replace(/^ *> ?/gm,"");this.token(cap,top,true);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i<l;i++){item=cap[i];space=item.length;item=item.replace(/^ *([*+-]|\d+\.) +/,"");if(~item.indexOf("\n ")){space-=item.length;item=!this.options.pedantic?item.replace(new RegExp("^ {1,"+space+"}","gm"),""):item.replace(/^ {1,4}/gm,"")}if(this.options.smartLists&&i!==l-1){b=block.bullet.exec(cap[i+1])[0];if(bull!==b&&!(bull.length>1&&b.length>1)){src=cap.slice(i+1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item.charAt(item.length-1)==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false,bq);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:cap[1]==="pre"||cap[1]==="script"||cap[1]==="style",text:cap[0]});continue}if(!bq&&top&&(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i<item.align.length;i++){if(/^ *-+: *$/.test(item.align[i])){item.align[i]="right"}else if(/^ *:-+: *$/.test(item.align[i])){item.align[i]="center"}else if(/^ *:-+ *$/.test(item.align[i])){item.align[i]="left"}else{item.align[i]=null}}for(i=0;i<item.cells.length;i++){item.cells[i]=item.cells[i].replace(/^ *\| *| *\| *$/g,"").split(/ *\| */)}this.tokens.push(item);continue}if(top&&(cap=this.rules.paragraph.exec(src))){src=src.substring(cap[0].length);this.tokens.push({type:"paragraph",text:cap[1].charAt(cap[1].length-1)==="\n"?cap[1].slice(0,-1):cap[1]});continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"text",text:cap[0]});continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return this.tokens};var inline={escape:/^\\([\\`*{}\[\]()#+\-.!_>])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/};inline._inside=/(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;inline._href=/\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;this.renderer=this.options.renderer||new Renderer;this.renderer.options=this.options;if(!this.links){throw new Error("Tokens array requires a `links` property.")}if(this.options.gfm){if(this.options.breaks){this.rules=inline.breaks}else{this.rules=inline.gfm}}else if(this.options.pedantic){this.rules=inline.pedantic}}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1].charAt(6)===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=this.renderer.link(href,null,text);continue}if(!this.inLink&&(cap=this.rules.url.exec(src))){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=this.renderer.link(href,null,text);continue}if(cap=this.rules.tag.exec(src)){if(!this.inLink&&/^<a /i.test(cap[0])){this.inLink=true}else if(this.inLink&&/^<\/a>/i.test(cap[0])){this.inLink=false}src=src.substring(cap[0].length);out+=this.options.sanitize?escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);this.inLink=true;out+=this.outputLink(cap,{href:cap[2],title:cap[3]});this.inLink=false;continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0].charAt(0);src=cap[0].substring(1)+src;continue}this.inLink=true;out+=this.outputLink(cap,link);this.inLink=false;continue}if(cap=this.rules.strong.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.strong(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.em(this.output(cap[2]||cap[1]));continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.codespan(escape(cap[2],true));continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.br();continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=this.renderer.del(this.output(cap[1]));continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=escape(this.smartypants(cap[0]));continue}if(src){throw new Error("Infinite loop on byte: "+src.charCodeAt(0))}}return out};InlineLexer.prototype.outputLink=function(cap,link){var href=escape(link.href),title=link.title?escape(link.title):null;return cap[0].charAt(0)!=="!"?this.renderer.link(href,title,this.output(cap[1])):this.renderer.image(href,title,escape(cap[1]))};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/--/g,"—").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…")};InlineLexer.prototype.mangle=function(text){var out="",l=text.length,i=0,ch;for(;i<l;i++){ch=text.charCodeAt(i);if(Math.random()>.5){ch="x"+ch.toString(16)}out+="&#"+ch+";"}return out};function Renderer(options){this.options=options||{}}Renderer.prototype.code=function(code,lang,escaped){if(this.options.highlight){var out=this.options.highlight(code,lang);if(out!=null&&out!==code){escaped=true;code=out}}if(!lang){return"<pre><code>"+(escaped?code:escape(code,true))+"\n</code></pre>"}return'<pre><code class="'+this.options.langPrefix+escape(lang,true)+'">'+(escaped?code:escape(code,true))+"\n</code></pre>\n"};Renderer.prototype.blockquote=function(quote){return"<blockquote>\n"+quote+"</blockquote>\n"};Renderer.prototype.html=function(html){return html};Renderer.prototype.heading=function(text,level,raw){return"<h"+level+' id="'+this.options.headerPrefix+raw.toLowerCase().replace(/[^\w]+/g,"-")+'">'+text+"</h"+level+">\n"};Renderer.prototype.hr=function(){return this.options.xhtml?"<hr/>\n":"<hr>\n"};Renderer.prototype.list=function(body,ordered){var type=ordered?"ol":"ul";return"<"+type+">\n"+body+"</"+type+">\n"};Renderer.prototype.listitem=function(text){return"<li>"+text+"</li>\n"};Renderer.prototype.paragraph=function(text){return"<p>"+text+"</p>\n"};Renderer.prototype.table=function(header,body){return"<table>\n"+"<thead>\n"+header+"</thead>\n"+"<tbody>\n"+body+"</tbody>\n"+"</table>\n"};Renderer.prototype.tablerow=function(content){return"<tr>\n"+content+"</tr>\n"};Renderer.prototype.tablecell=function(content,flags){var type=flags.header?"th":"td";var tag=flags.align?"<"+type+' style="text-align:'+flags.align+'">':"<"+type+">";return tag+content+"</"+type+">\n"};Renderer.prototype.strong=function(text){return"<strong>"+text+"</strong>"};Renderer.prototype.em=function(text){return"<em>"+text+"</em>"};Renderer.prototype.codespan=function(text){return"<code>"+text+"</code>"};Renderer.prototype.br=function(){return this.options.xhtml?"<br/>":"<br>"};Renderer.prototype.del=function(text){return"<del>"+text+"</del>"};Renderer.prototype.link=function(href,title,text){if(this.options.sanitize){try{var prot=decodeURIComponent(unescape(href)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(prot.indexOf("javascript:")===0){return""}}var out='<a href="'+href+'"';if(title){out+=' title="'+title+'"'}out+=">"+text+"</a>";return out};Renderer.prototype.image=function(href,title,text){var out='<img src="'+href+'" alt="'+text+'"';if(title){out+=' title="'+title+'"'}out+=this.options.xhtml?"/>":">";return out};function Parser(options){this.tokens=[];this.token=null;this.options=options||marked.defaults;this.options.renderer=this.options.renderer||new Renderer;this.renderer=this.options.renderer;this.renderer.options=this.options}Parser.parse=function(src,options,renderer){var parser=new Parser(options,renderer);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options,this.renderer);this.tokens=src.reverse();var out="";while(this.next()){out+=this.tok()}return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;while(this.peek().type==="text"){body+="\n"+this.next().text}return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case"space":{return""}case"hr":{return this.renderer.hr()}case"heading":{return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text)}case"code":{return this.renderer.code(this.token.text,this.token.lang,this.token.escaped)}case"table":{var header="",body="",i,row,cell,flags,j;cell="";for(i=0;i<this.token.header.length;i++){flags={header:true,align:this.token.align[i]};cell+=this.renderer.tablecell(this.inline.output(this.token.header[i]),{header:true,align:this.token.align[i]})}header+=this.renderer.tablerow(cell);for(i=0;i<this.token.cells.length;i++){row=this.token.cells[i];cell="";for(j=0;j<row.length;j++){cell+=this.renderer.tablecell(this.inline.output(row[j]),{header:false,align:this.token.align[j]})}body+=this.renderer.tablerow(cell)}return this.renderer.table(header,body)}case"blockquote_start":{var body="";while(this.next().type!=="blockquote_end"){body+=this.tok()}return this.renderer.blockquote(body)}case"list_start":{var body="",ordered=this.token.ordered;while(this.next().type!=="list_end"){body+=this.tok()}return this.renderer.list(body,ordered)}case"list_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.token.type==="text"?this.parseText():this.tok()}return this.renderer.listitem(body)}case"loose_item_start":{var body="";while(this.next().type!=="list_item_end"){body+=this.tok()}return this.renderer.listitem(body)}case"html":{var html=!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;return this.renderer.html(html)}case"paragraph":{return this.renderer.paragraph(this.inline.output(this.token.text))}case"text":{return this.renderer.paragraph(this.parseText())}}};function escape(html,encode){return html.replace(!encode?/&(?!#?\w+;)/g:/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function unescape(html){return html.replace(/&([#\w]+);/g,function(_,n){n=n.toLowerCase();if(n==="colon")return":";if(n.charAt(0)==="#"){return n.charAt(1)==="x"?String.fromCharCode(parseInt(n.substring(2),16)):String.fromCharCode(+n.substring(1))}return""})}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=1,target,key;for(;i<arguments.length;i++){target=arguments[i];for(key in target){if(Object.prototype.hasOwnProperty.call(target,key)){obj[key]=target[key]}}}return obj}function marked(src,opt,callback){if(callback||typeof opt==="function"){if(!callback){callback=opt;opt=null}opt=merge({},marked.defaults,opt||{});var highlight=opt.highlight,tokens,pending,i=0;try{tokens=Lexer.lex(src,opt)}catch(e){return callback(e)}pending=tokens.length;var done=function(){var out,err;try{out=Parser.parse(tokens,opt)}catch(e){err=e}opt.highlight=highlight;return err?callback(err):callback(null,out)};if(!highlight||highlight.length<3){return done()}delete opt.highlight;if(!pending)return done();for(;i<tokens.length;i++){(function(token){if(token.type!=="code"){return--pending||done()}return highlight(token.text,token.lang,function(err,code){if(code==null||code===token.text){return--pending||done()}token.text=code;token.escaped=true;--pending||done()})})(tokens[i])}return}try{if(opt)opt=merge({},marked.defaults,opt);return Parser.parse(Lexer.lex(src,opt),opt)}catch(e){e.message+="\nPlease report this to https://github.com/chjj/marked.";if((opt||marked.defaults).silent){return"<p>An error occured:</p><pre>"+escape(e.message+"",true)+"</pre>"}throw e}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,smartLists:false,silent:false,highlight:null,langPrefix:"lang-",smartypants:false,headerPrefix:"",renderer:new Renderer,xhtml:false};marked.Parser=Parser;marked.parser=Parser.parse;marked.Renderer=Renderer;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;marked.parse=marked;if(typeof exports==="object"){module.exports=marked}else if(typeof define==="function"&&define.amd){define(function(){return marked})}else{this.marked=marked}}).call(function(){return this||(typeof window!=="undefined"?window:global)}());
\ No newline at end of file
diff --git a/examples/classic/markdown/style.css b/examples/markdown/style.css
similarity index 100%
rename from examples/classic/markdown/style.css
rename to examples/markdown/style.css
diff --git a/examples/modal/index.html b/examples/modal/index.html
new file mode 100644
index 00000000000..04599826a0a
--- /dev/null
+++ b/examples/modal/index.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Vue.js Modal Example</title>
+    <script src="../../dist/vue.js"></script>
+    <link rel="stylesheet" href="modal.css">
+  </head>
+  <body>
+    <!-- template for the modal component -->
+    <script type="x/template" id="modal-template">
+      <div class="modal-mask" v-show="show" transition="modal">
+        <div class="modal-wrapper">
+          <div class="modal-container">
+
+            <div class="modal-header">
+              <slot name="header">
+                default header
+              </slot>
+            </div>
+            
+            <div class="modal-body">
+              <slot name="body">
+                default body
+              </slot>
+            </div>
+
+            <div class="modal-footer">
+              <slot name="footer">
+                default footer
+                <button class="modal-default-button"
+                  @click="show = false">
+                  OK
+                </button>
+              </slot>
+            </div>
+          </div>
+        </div>
+      </div>
+    </script>
+
+    <script>
+      // register modal component
+      Vue.component('modal', {
+        template: '#modal-template',
+        props: {
+          show: {
+            type: Boolean,
+            required: true,
+            twoWay: true    
+          }
+        }
+      })
+    </script>
+
+    <!-- app -->
+    <div id="app">
+      <button id="show-modal" @click="showModal = true">Show Modal</button>
+      <!-- use the modal component, pass in the prop -->
+      <modal :show.sync="showModal">
+        <!--
+          you can use custom content here to overwrite
+          default content
+        -->
+        <h3 slot="header">custom header</h3>
+      </modal>
+    </div>
+
+    <script>
+      // start app
+      new Vue({
+        el: '#app',
+        data: {
+          showModal: false
+        }
+      })
+    </script>
+  </body>
+</html>
diff --git a/examples/classic/modal/style.css b/examples/modal/modal.css
similarity index 86%
rename from examples/classic/modal/style.css
rename to examples/modal/modal.css
index 45563156a1a..0fdde9790c2 100644
--- a/examples/classic/modal/style.css
+++ b/examples/modal/modal.css
@@ -40,7 +40,7 @@
 }
 
 /*
- * The following styles are auto-applied to elements with
+ * the following styles are auto-applied to elements with
  * transition="modal" when their visibility is toggled
  * by Vue.js.
  *
@@ -48,16 +48,12 @@
  * these styles.
  */
 
-.modal-enter {
-  opacity: 0;
-}
-
-.modal-leave-to {
+.modal-enter, .modal-leave {
   opacity: 0;
 }
 
 .modal-enter .modal-container,
-.modal-leave-to .modal-container {
+.modal-leave .modal-container {
   -webkit-transform: scale(1.1);
   transform: scale(1.1);
 }
diff --git a/examples/select2/index.html b/examples/select2/index.html
new file mode 100644
index 00000000000..f313853d00d
--- /dev/null
+++ b/examples/select2/index.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Vue.js custom directive integration example (select2)</title>
+    <script src="../../dist/vue.js"></script>
+    <script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
+    <link href="http://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet">
+    <script src="http://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
+    <style>
+      select {
+        min-width: 300px;
+      }
+    </style>
+  </head>
+  <body>
+
+    <div id="el">
+      <p>Selected: {{selected}}</p>
+      <select v-select="selected" :options="options">
+        <option value="0">default</option>
+      </select>
+    </div>
+
+    <script>
+    Vue.directive('select', {
+      twoWay: true,
+
+      params: ['options'],
+
+      bind: function () {
+        var self = this
+        $(this.el)
+          .select2({
+            data: this.params.options
+          })
+          .on('change', function () {
+            self.set(this.value)
+          })
+      },
+      update: function (value) {
+        $(this.el).val(value).trigger('change')
+      },
+      unbind: function () {
+        $(this.el).off().select2('destroy')
+      }
+    })
+
+    var vm = new Vue({
+      el: '#el',
+      data: {
+        selected: 0,
+        options: [
+          { id: 1, text: 'hello' },
+          { id: 2, text: 'what' }
+        ]
+      }
+    })
+    </script>
+  </body>
+</html>
diff --git a/examples/classic/svg/index.html b/examples/svg/index.html
similarity index 80%
rename from examples/classic/svg/index.html
rename to examples/svg/index.html
index 75d229638bb..155e55e63c5 100644
--- a/examples/classic/svg/index.html
+++ b/examples/svg/index.html
@@ -2,10 +2,9 @@
 <html lang="en">
   <head>
     <meta charset="utf-8">
-    <title>Vue.js SVG graph example</title>
+    <title>Vue.js SVG example</title>
     <link rel="stylesheet" href="style.css">
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../../dist/vue.min.js"></script>
+    <script src="../../dist/vue.js"></script>
   </head>
   <body>
 
@@ -15,9 +14,9 @@
         <polygon :points="points"></polygon>
         <circle cx="100" cy="100" r="80"></circle>
         <axis-label
-          v-for="(stat, index) in stats"
+          v-for="stat in stats"
           :stat="stat"
-          :index="index"
+          :index="$index"
           :total="stats.length">
         </axis-label>
       </g>
@@ -39,15 +38,15 @@
         <label>{{stat.label}}</label>
         <input type="range" v-model="stat.value" min="0" max="100">
         <span>{{stat.value}}</span>
-        <button @click="remove(stat)" class="remove">X</button>
+        <button @click="remove(stat)">X</button>
       </div>
       <form id="add">
         <input name="newlabel" v-model="newLabel">
         <button @click="add">Add a Stat</button>
       </form>
-      <pre id="raw">{{ stats }}</pre>
+      <pre id="raw">{{stats | json}}</pre>
     </div>
-
+      
     <p style="font-size:12px">* input[type="range"] requires IE10 or above.</p>
 
     <script src="svg.js"></script>
diff --git a/examples/classic/svg/style.css b/examples/svg/style.css
similarity index 100%
rename from examples/classic/svg/style.css
rename to examples/svg/style.css
diff --git a/examples/classic/svg/svg.js b/examples/svg/svg.js
similarity index 61%
rename from examples/classic/svg/svg.js
rename to examples/svg/svg.js
index c6df94582ef..9c308705c0d 100644
--- a/examples/classic/svg/svg.js
+++ b/examples/svg/svg.js
@@ -1,5 +1,5 @@
 // The raw data to observe
-var globalStats = [
+var stats = [
   { label: 'A', value: 100 },
   { label: 'B', value: 100 },
   { label: 'C', value: 100 },
@@ -8,20 +8,19 @@ var globalStats = [
   { label: 'F', value: 100 }
 ]
 
-// A reusable polygon graph component
+// A resusable polygon graph component
 Vue.component('polygraph', {
   props: ['stats'],
   template: '#polygraph-template',
+  replace: true,
   computed: {
     // a computed property for the polygon's points
     points: function () {
       var total = this.stats.length
-      return this.stats
-        .map(function (stat, i) {
-          var point = valueToPoint(stat.value, i, total)
-          return point.x + ',' + point.y
-        })
-        .join(' ')
+      return this.stats.map(function (stat, i) {
+        var point = valueToPoint(stat.value, i, total)
+        return point.x + ',' + point.y
+      }).join(' ')
     }
   },
   components: {
@@ -33,9 +32,14 @@ Vue.component('polygraph', {
         total: Number
       },
       template: '#axis-label-template',
+      replace: true,
       computed: {
         point: function () {
-          return valueToPoint(+this.stat.value + 10, this.index, this.total)
+          return valueToPoint(
+            +this.stat.value + 10,
+            this.index,
+            this.total
+          )
         }
       }
     }
@@ -43,14 +47,14 @@ Vue.component('polygraph', {
 })
 
 // math helper...
-function valueToPoint(value, index, total) {
-  var x = 0
-  var y = -value * 0.8
-  var angle = ((Math.PI * 2) / total) * index
-  var cos = Math.cos(angle)
-  var sin = Math.sin(angle)
-  var tx = x * cos - y * sin + 100
-  var ty = x * sin + y * cos + 100
+function valueToPoint (value, index, total) {
+  var x     = 0
+  var y     = -value * 0.8
+  var angle = Math.PI * 2 / total * index
+  var cos   = Math.cos(angle)
+  var sin   = Math.sin(angle)
+  var tx    = x * cos - y * sin + 100
+  var ty    = x * sin + y * cos + 100
   return {
     x: tx,
     y: ty
@@ -62,7 +66,7 @@ new Vue({
   el: '#demo',
   data: {
     newLabel: '',
-    stats: globalStats
+    stats: stats
   },
   methods: {
     add: function (e) {
@@ -76,9 +80,9 @@ new Vue({
     },
     remove: function (stat) {
       if (this.stats.length > 3) {
-        this.stats.splice(this.stats.indexOf(stat), 1)
+        this.stats.$remove(stat)
       } else {
-        alert("Can't delete more!")
+        alert('Can\'t delete more!')
       }
     }
   }
diff --git a/examples/todomvc/index.html b/examples/todomvc/index.html
new file mode 100644
index 00000000000..1ab98b00b20
--- /dev/null
+++ b/examples/todomvc/index.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<html data-framework="vue">
+	<head>
+		<meta charset="utf-8">
+		<title>Vue.js • TodoMVC</title>
+		<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
+		<style> [v-cloak] { display: none; } </style>
+	</head>
+	<body>
+		<section class="todoapp">
+			<header class="header">
+				<h1>todos</h1>
+				<input class="new-todo"
+					autofocus autocomplete="off"
+					placeholder="What needs to be done?"
+					v-model="newTodo"
+					@keyup.enter="addTodo">
+			</header>
+			<section class="main" v-show="todos.length" v-cloak>
+				<input class="toggle-all" type="checkbox" v-model="allDone">
+				<ul class="todo-list">
+					<li class="todo"
+						v-for="todo in filteredTodos"
+						:class="{completed: todo.completed, editing: todo == editedTodo}">
+						<div class="view">
+							<input class="toggle" type="checkbox" v-model="todo.completed">
+							<label @dblclick="editTodo(todo)">{{todo.title}}</label>
+							<button class="destroy" @click="removeTodo(todo)"></button>
+						</div>
+						<input class="edit" type="text"
+							v-model="todo.title"
+							v-todo-focus="todo == editedTodo"
+							@blur="doneEdit(todo)"
+							@keyup.enter="doneEdit(todo)"
+							@keyup.esc="cancelEdit(todo)">
+					</li>
+				</ul>
+			</section>
+			<footer class="footer" v-show="todos.length" v-cloak>
+				<span class="todo-count">
+					<strong v-text="remaining"></strong> {{remaining | pluralize 'item'}} left
+				</span>
+				<ul class="filters">
+					<li><a href="#/all" :class="{selected: visibility == 'all'}">All</a></li>
+					<li><a href="#/active" :class="{selected: visibility == 'active'}">Active</a></li>
+					<li><a href="#/completed" :class="{selected: visibility == 'completed'}">Completed</a></li>
+				</ul>
+				<button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">
+					Clear completed
+				</button>
+			</footer>
+		</section>
+		<footer class="info">
+			<p>Double-click to edit a todo</p>
+			<p>Written by <a href="http://evanyou.me">Evan You</a></p>
+			<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
+		</footer>
+
+		<!-- testing/benchmark only -->
+		<script>
+		var isPhantom = navigator.userAgent.indexOf('PhantomJS') > -1
+		if (isPhantom) {
+			localStorage.clear()
+		} else {
+			var now = window.performance && window.performance.now
+				? function () { return window.performance.now() }
+				: Date.now
+			var metrics = { beforeLoad: now() }
+		}
+		</script>
+		<!-- end testing/bench -->
+
+		<script src="../../dist/vue.js"></script>
+		<script>metrics.afterLoad = now()</script>
+		<script src="node_modules/director/build/director.js"></script>
+		<script src="js/store.js"></script>
+		<script>metrics.beforeRender = now()</script>
+		<script src="js/app.js"></script>
+		<script src="js/routes.js"></script>
+		<script>metrics.afterRender = now()</script>
+		<script src="perf.js"></script>
+	</body>
+</html>
diff --git a/examples/todomvc/js/app.js b/examples/todomvc/js/app.js
new file mode 100644
index 00000000000..dd4fc21a2c4
--- /dev/null
+++ b/examples/todomvc/js/app.js
@@ -0,0 +1,126 @@
+/*global Vue, todoStorage */
+
+(function (exports) {
+
+	'use strict';
+
+	var filters = {
+		all: function (todos) {
+			return todos;
+		},
+		active: function (todos) {
+			return todos.filter(function (todo) {
+				return !todo.completed;
+			});
+		},
+		completed: function (todos) {
+			return todos.filter(function (todo) {
+				return todo.completed;
+			});
+		}
+	};
+
+	exports.app = new Vue({
+
+		// the root element that will be compiled
+		el: '.todoapp',
+
+		// app initial state
+		data: {
+			todos: todoStorage.fetch(),
+			newTodo: '',
+			editedTodo: null,
+			visibility: 'all'
+		},
+
+		// watch todos change for localStorage persistence
+		watch: {
+			todos: {
+				handler: function (todos) {
+				  todoStorage.save(todos);
+				},
+				deep: true
+			}
+		},
+
+		// computed properties
+		// http://vuejs.org/guide/computed.html
+		computed: {
+			filteredTodos: function () {
+				return filters[this.visibility](this.todos);
+			},
+			remaining: function () {
+				return filters.active(this.todos).length;
+			},
+			allDone: {
+				get: function () {
+					return this.remaining === 0;
+				},
+				set: function (value) {
+					this.todos.forEach(function (todo) {
+						todo.completed = value;
+					});
+				}
+			}
+		},
+
+		// methods that implement data logic.
+		// note there's no DOM manipulation here at all.
+		methods: {
+
+			addTodo: function () {
+				var value = this.newTodo && this.newTodo.trim();
+				if (!value) {
+					return;
+				}
+				this.todos.push({ title: value, completed: false });
+				this.newTodo = '';
+			},
+
+			removeTodo: function (todo) {
+				this.todos.$remove(todo);
+			},
+
+			editTodo: function (todo) {
+				this.beforeEditCache = todo.title;
+				this.editedTodo = todo;
+			},
+
+			doneEdit: function (todo) {
+				if (!this.editedTodo) {
+					return;
+				}
+				this.editedTodo = null;
+				todo.title = todo.title.trim();
+				if (!todo.title) {
+					this.removeTodo(todo);
+				}
+			},
+
+			cancelEdit: function (todo) {
+				this.editedTodo = null;
+				todo.title = this.beforeEditCache;
+			},
+
+			removeCompleted: function () {
+				this.todos = filters.active(this.todos);
+			}
+		},
+
+		// a custom directive to wait for the DOM to be updated
+		// before focusing on the input field.
+		// http://vuejs.org/guide/custom-directive.html
+		directives: {
+			'todo-focus': function (value) {
+				if (!value) {
+					return;
+				}
+				var el = this.el;
+				Vue.nextTick(function () {
+					el.focus();
+				});
+			}
+		}
+	});
+
+})(window);
diff --git a/examples/todomvc/js/routes.js b/examples/todomvc/js/routes.js
new file mode 100644
index 00000000000..b5556053b23
--- /dev/null
+++ b/examples/todomvc/js/routes.js
@@ -0,0 +1,24 @@
+/*global app, Router */
+
+(function (app, Router) {
+
+	'use strict';
+
+	var router = new Router();
+
+	['all', 'active', 'completed'].forEach(function (visibility) {
+		router.on(visibility, function () {
+			app.visibility = visibility;
+		});
+	});
+
+	router.configure({
+		notfound: function () {
+			window.location.hash = '';
+			app.visibility = 'all';
+		}
+	});
+
+	router.init();
+
+})(app, Router);
diff --git a/examples/todomvc/js/store.js b/examples/todomvc/js/store.js
new file mode 100644
index 00000000000..4a4b12b00f3
--- /dev/null
+++ b/examples/todomvc/js/store.js
@@ -0,0 +1,18 @@
+/*jshint unused:false */
+
+(function (exports) {
+
+	'use strict';
+
+	var STORAGE_KEY = 'todos-vuejs';
+
+	exports.todoStorage = {
+		fetch: function () {
+			return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
+		},
+		save: function (todos) {
+			localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
+		}
+	};
+
+})(window);
diff --git a/examples/todomvc/node_modules/director/build/director.js b/examples/todomvc/node_modules/director/build/director.js
new file mode 100644
index 00000000000..10388787235
--- /dev/null
+++ b/examples/todomvc/node_modules/director/build/director.js
@@ -0,0 +1,725 @@
+
+
+//
+// Generated on Tue Dec 16 2014 12:13:47 GMT+0100 (CET) by Charlie Robbins, Paolo Fragomeni & the Contributors (Using Codesurgeon).
+// Version 1.2.6
+//
+
+(function (exports) {
+
+/*
+ * browser.js: Browser specific functionality for director.
+ *
+ * (C) 2011, Charlie Robbins, Paolo Fragomeni, & the Contributors.
+ * MIT LICENSE
+ *
+ */
+
+var dloc = document.location;
+
+function dlocHashEmpty() {
+  // Non-IE browsers return '' when the address bar shows '#'; Director's logic
+  // assumes both mean empty.
+  return dloc.hash === '' || dloc.hash === '#';
+}
+
+var listener = {
+  mode: 'modern',
+  hash: dloc.hash,
+  history: false,
+
+  check: function () {
+    var h = dloc.hash;
+    if (h != this.hash) {
+      this.hash = h;
+      this.onHashChanged();
+    }
+  },
+
+  fire: function () {
+    if (this.mode === 'modern') {
+      this.history === true ? window.onpopstate() : window.onhashchange();
+    }
+    else {
+      this.onHashChanged();
+    }
+  },
+
+  init: function (fn, history) {
+    var self = this;
+    this.history = history;
+
+    if (!Router.listeners) {
+      Router.listeners = [];
+    }
+
+    function onchange(onChangeEvent) {
+      for (var i = 0, l = Router.listeners.length; i < l; i++) {
+        Router.listeners[i](onChangeEvent);
+      }
+    }
+
+    //note IE8 is being counted as 'modern' because it has the hashchange event
+    if ('onhashchange' in window && (document.documentMode === undefined
+      || document.documentMode > 7)) {
+      // At least for now HTML5 history is available for 'modern' browsers only
+      if (this.history === true) {
+        // There is an old bug in Chrome that causes onpopstate to fire even
+        // upon initial page load. Since the handler is run manually in init(),
+        // this would cause Chrome to run it twise. Currently the only
+        // workaround seems to be to set the handler after the initial page load
+        // http://code.google.com/p/chromium/issues/detail?id=63040
+        setTimeout(function() {
+          window.onpopstate = onchange;
+        }, 500);
+      }
+      else {
+        window.onhashchange = onchange;
+      }
+      this.mode = 'modern';
+    }
+    else {
+      //
+      // IE support, based on a concept by Erik Arvidson ...
+      //
+      var frame = document.createElement('iframe');
+      frame.id = 'state-frame';
+      frame.style.display = 'none';
+      document.body.appendChild(frame);
+      this.writeFrame('');
+
+      if ('onpropertychange' in document && 'attachEvent' in document) {
+        document.attachEvent('onpropertychange', function () {
+          if (event.propertyName === 'location') {
+            self.check();
+          }
+        });
+      }
+
+      window.setInterval(function () { self.check(); }, 50);
+
+      this.onHashChanged = onchange;
+      this.mode = 'legacy';
+    }
+
+    Router.listeners.push(fn);
+
+    return this.mode;
+  },
+
+  destroy: function (fn) {
+    if (!Router || !Router.listeners) {
+      return;
+    }
+
+    var listeners = Router.listeners;
+
+    for (var i = listeners.length - 1; i >= 0; i--) {
+      if (listeners[i] === fn) {
+        listeners.splice(i, 1);
+      }
+    }
+  },
+
+  setHash: function (s) {
+    // Mozilla always adds an entry to the history
+    if (this.mode === 'legacy') {
+      this.writeFrame(s);
+    }
+
+    if (this.history === true) {
+      window.history.pushState({}, document.title, s);
+      // Fire an onpopstate event manually since pushing does not obviously
+      // trigger the pop event.
+      this.fire();
+    } else {
+      dloc.hash = (s[0] === '/') ? s : '/' + s;
+    }
+    return this;
+  },
+
+  writeFrame: function (s) {
+    // IE support...
+    var f = document.getElementById('state-frame');
+    var d = f.contentDocument || f.contentWindow.document;
+    d.open();
+    d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
+    d.close();
+  },
+
+  syncHash: function () {
+    // IE support...
+    var s = this._hash;
+    if (s != dloc.hash) {
+      dloc.hash = s;
+    }
+    return this;
+  },
+
+  onHashChanged: function () {}
+};
+
+var Router = exports.Router = function (routes) {
+  if (!(this instanceof Router)) return new Router(routes);
+
+  this.params   = {};
+  this.routes   = {};
+  this.methods  = ['on', 'once', 'after', 'before'];
+  this.scope    = [];
+  this._methods = {};
+
+  this._insert = this.insert;
+  this.insert = this.insertEx;
+
+  this.historySupport = (window.history != null ? window.history.pushState : null) != null
+
+  this.configure();
+  this.mount(routes || {});
+};
+
+Router.prototype.init = function (r) {
+  var self = this
+    , routeTo;
+  this.handler = function(onChangeEvent) {
+    var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
+    var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
+    self.dispatch('on', url.charAt(0) === '/' ? url : '/' + url);
+  };
+
+  listener.init(this.handler, this.history);
+
+  if (this.history === false) {
+    if (dlocHashEmpty() && r) {
+      dloc.hash = r;
+    } else if (!dlocHashEmpty()) {
+      self.dispatch('on', '/' + dloc.hash.replace(/^(#\/|#|\/)/, ''));
+    }
+  }
+  else {
+    if (this.convert_hash_in_init) {
+      // Use hash as route
+      routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
+      if (routeTo) {
+        window.history.replaceState({}, document.title, routeTo);
+      }
+    }
+    else {
+      // Use canonical url
+      routeTo = this.getPath();
+    }
+
+    // Router has been initialized, but due to the chrome bug it will not
+    // yet actually route HTML5 history state changes. Thus, decide if should route.
+    if (routeTo || this.run_in_init === true) {
+      this.handler();
+    }
+  }
+
+  return this;
+};
+
+Router.prototype.explode = function () {
+  var v = this.history === true ? this.getPath() : dloc.hash;
+  if (v.charAt(1) === '/') { v=v.slice(1) }
+  return v.slice(1, v.length).split("/");
+};
+
+Router.prototype.setRoute = function (i, v, val) {
+  var url = this.explode();
+
+  if (typeof i === 'number' && typeof v === 'string') {
+    url[i] = v;
+  }
+  else if (typeof val === 'string') {
+    url.splice(i, v, s);
+  }
+  else {
+    url = [i];
+  }
+
+  listener.setHash(url.join('/'));
+  return url;
+};
+
+//
+// ### function insertEx(method, path, route, parent)
+// #### @method {string} Method to insert the specific `route`.
+// #### @path {Array} Parsed path to insert the `route` at.
+// #### @route {Array|function} Route handlers to insert.
+// #### @parent {Object} **Optional** Parent "routes" to insert into.
+// insert a callback that will only occur once per the matched route.
+//
+Router.prototype.insertEx = function(method, path, route, parent) {
+  if (method === "once") {
+    method = "on";
+    route = function(route) {
+      var once = false;
+      return function() {
+        if (once) return;
+        once = true;
+        return route.apply(this, arguments);
+      };
+    }(route);
+  }
+  return this._insert(method, path, route, parent);
+};
+
+Router.prototype.getRoute = function (v) {
+  var ret = v;
+
+  if (typeof v === "number") {
+    ret = this.explode()[v];
+  }
+  else if (typeof v === "string"){
+    var h = this.explode();
+    ret = h.indexOf(v);
+  }
+  else {
+    ret = this.explode();
+  }
+
+  return ret;
+};
+
+Router.prototype.destroy = function () {
+  listener.destroy(this.handler);
+  return this;
+};
+
+Router.prototype.getPath = function () {
+  var path = window.location.pathname;
+  if (path.substr(0, 1) !== '/') {
+    path = '/' + path;
+  }
+  return path;
+};
+function _every(arr, iterator) {
+  for (var i = 0; i < arr.length; i += 1) {
+    if (iterator(arr[i], i, arr) === false) {
+      return;
+    }
+  }
+}
+
+function _flatten(arr) {
+  var flat = [];
+  for (var i = 0, n = arr.length; i < n; i++) {
+    flat = flat.concat(arr[i]);
+  }
+  return flat;
+}
+
+function _asyncEverySeries(arr, iterator, callback) {
+  if (!arr.length) {
+    return callback();
+  }
+  var completed = 0;
+  (function iterate() {
+    iterator(arr[completed], function(err) {
+      if (err || err === false) {
+        callback(err);
+        callback = function() {};
+      } else {
+        completed += 1;
+        if (completed === arr.length) {
+          callback();
+        } else {
+          iterate();
+        }
+      }
+    });
+  })();
+}
+
+function paramifyString(str, params, mod) {
+  mod = str;
+  for (var param in params) {
+    if (params.hasOwnProperty(param)) {
+      mod = params[param](str);
+      if (mod !== str) {
+        break;
+      }
+    }
+  }
+  return mod === str ? "([._a-zA-Z0-9-%()]+)" : mod;
+}
+
+function regifyString(str, params) {
+  var matches, last = 0, out = "";
+  while (matches = str.substr(last).match(/[^\w\d\- %@&]*\*[^\w\d\- %@&]*/)) {
+    last = matches.index + matches[0].length;
+    matches[0] = matches[0].replace(/^\*/, "([_.()!\\ %@&a-zA-Z0-9-]+)");
+    out += str.substr(0, matches.index) + matches[0];
+  }
+  str = out += str.substr(last);
+  var captures = str.match(/:([^\/]+)/ig), capture, length;
+  if (captures) {
+    length = captures.length;
+    for (var i = 0; i < length; i++) {
+      capture = captures[i];
+      if (capture.slice(0, 2) === "::") {
+        str = capture.slice(1);
+      } else {
+        str = str.replace(capture, paramifyString(capture, params));
+      }
+    }
+  }
+  return str;
+}
+
+function terminator(routes, delimiter, start, stop) {
+  var last = 0, left = 0, right = 0, start = (start || "(").toString(), stop = (stop || ")").toString(), i;
+  for (i = 0; i < routes.length; i++) {
+    var chunk = routes[i];
+    if (chunk.indexOf(start, last) > chunk.indexOf(stop, last) || ~chunk.indexOf(start, last) && !~chunk.indexOf(stop, last) || !~chunk.indexOf(start, last) && ~chunk.indexOf(stop, last)) {
+      left = chunk.indexOf(start, last);
+      right = chunk.indexOf(stop, last);
+      if (~left && !~right || !~left && ~right) {
+        var tmp = routes.slice(0, (i || 1) + 1).join(delimiter);
+        routes = [ tmp ].concat(routes.slice((i || 1) + 1));
+      }
+      last = (right > left ? right : left) + 1;
+      i = 0;
+    } else {
+      last = 0;
+    }
+  }
+  return routes;
+}
+
+var QUERY_SEPARATOR = /\?.*/;
+
+Router.prototype.configure = function(options) {
+  options = options || {};
+  for (var i = 0; i < this.methods.length; i++) {
+    this._methods[this.methods[i]] = true;
+  }
+  this.recurse = options.recurse || this.recurse || false;
+  this.async = options.async || false;
+  this.delimiter = options.delimiter || "/";
+  this.strict = typeof options.strict === "undefined" ? true : options.strict;
+  this.notfound = options.notfound;
+  this.resource = options.resource;
+  this.history = options.html5history && this.historySupport || false;
+  this.run_in_init = this.history === true && options.run_handler_in_init !== false;
+  this.convert_hash_in_init = this.history === true && options.convert_hash_in_init !== false;
+  this.every = {
+    after: options.after || null,
+    before: options.before || null,
+    on: options.on || null
+  };
+  return this;
+};
+
+Router.prototype.param = function(token, matcher) {
+  if (token[0] !== ":") {
+    token = ":" + token;
+  }
+  var compiled = new RegExp(token, "g");
+  this.params[token] = function(str) {
+    return str.replace(compiled, matcher.source || matcher);
+  };
+  return this;
+};
+
+Router.prototype.on = Router.prototype.route = function(method, path, route) {
+  var self = this;
+  if (!route && typeof path == "function") {
+    route = path;
+    path = method;
+    method = "on";
+  }
+  if (Array.isArray(path)) {
+    return path.forEach(function(p) {
+      self.on(method, p, route);
+    });
+  }
+  if (path.source) {
+    path = path.source.replace(/\\\//ig, "/");
+  }
+  if (Array.isArray(method)) {
+    return method.forEach(function(m) {
+      self.on(m.toLowerCase(), path, route);
+    });
+  }
+  path = path.split(new RegExp(this.delimiter));
+  path = terminator(path, this.delimiter);
+  this.insert(method, this.scope.concat(path), route);
+};
+
+Router.prototype.path = function(path, routesFn) {
+  var self = this, length = this.scope.length;
+  if (path.source) {
+    path = path.source.replace(/\\\//ig, "/");
+  }
+  path = path.split(new RegExp(this.delimiter));
+  path = terminator(path, this.delimiter);
+  this.scope = this.scope.concat(path);
+  routesFn.call(this, this);
+  this.scope.splice(length, path.length);
+};
+
+Router.prototype.dispatch = function(method, path, callback) {
+  var self = this, fns = this.traverse(method, path.replace(QUERY_SEPARATOR, ""), this.routes, ""), invoked = this._invoked, after;
+  this._invoked = true;
+  if (!fns || fns.length === 0) {
+    this.last = [];
+    if (typeof this.notfound === "function") {
+      this.invoke([ this.notfound ], {
+        method: method,
+        path: path
+      }, callback);
+    }
+    return false;
+  }
+  if (this.recurse === "forward") {
+    fns = fns.reverse();
+  }
+  function updateAndInvoke() {
+    self.last = fns.after;
+    self.invoke(self.runlist(fns), self, callback);
+  }
+  after = this.every && this.every.after ? [ this.every.after ].concat(this.last) : [ this.last ];
+  if (after && after.length > 0 && invoked) {
+    if (this.async) {
+      this.invoke(after, this, updateAndInvoke);
+    } else {
+      this.invoke(after, this);
+      updateAndInvoke();
+    }
+    return true;
+  }
+  updateAndInvoke();
+  return true;
+};
+
+Router.prototype.invoke = function(fns, thisArg, callback) {
+  var self = this;
+  var apply;
+  if (this.async) {
+    apply = function(fn, next) {
+      if (Array.isArray(fn)) {
+        return _asyncEverySeries(fn, apply, next);
+      } else if (typeof fn == "function") {
+        fn.apply(thisArg, (fns.captures || []).concat(next));
+      }
+    };
+    _asyncEverySeries(fns, apply, function() {
+      if (callback) {
+        callback.apply(thisArg, arguments);
+      }
+    });
+  } else {
+    apply = function(fn) {
+      if (Array.isArray(fn)) {
+        return _every(fn, apply);
+      } else if (typeof fn === "function") {
+        return fn.apply(thisArg, fns.captures || []);
+      } else if (typeof fn === "string" && self.resource) {
+        self.resource[fn].apply(thisArg, fns.captures || []);
+      }
+    };
+    _every(fns, apply);
+  }
+};
+
+Router.prototype.traverse = function(method, path, routes, regexp, filter) {
+  var fns = [], current, exact, match, next, that;
+  function filterRoutes(routes) {
+    if (!filter) {
+      return routes;
+    }
+    function deepCopy(source) {
+      var result = [];
+      for (var i = 0; i < source.length; i++) {
+        result[i] = Array.isArray(source[i]) ? deepCopy(source[i]) : source[i];
+      }
+      return result;
+    }
+    function applyFilter(fns) {
+      for (var i = fns.length - 1; i >= 0; i--) {
+        if (Array.isArray(fns[i])) {
+          applyFilter(fns[i]);
+          if (fns[i].length === 0) {
+            fns.splice(i, 1);
+          }
+        } else {
+          if (!filter(fns[i])) {
+            fns.splice(i, 1);
+          }
+        }
+      }
+    }
+    var newRoutes = deepCopy(routes);
+    newRoutes.matched = routes.matched;
+    newRoutes.captures = routes.captures;
+    newRoutes.after = routes.after.filter(filter);
+    applyFilter(newRoutes);
+    return newRoutes;
+  }
+  if (path === this.delimiter && routes[method]) {
+    next = [ [ routes.before, routes[method] ].filter(Boolean) ];
+    next.after = [ routes.after ].filter(Boolean);
+    next.matched = true;
+    next.captures = [];
+    return filterRoutes(next);
+  }
+  for (var r in routes) {
+    if (routes.hasOwnProperty(r) && (!this._methods[r] || this._methods[r] && typeof routes[r] === "object" && !Array.isArray(routes[r]))) {
+      current = exact = regexp + this.delimiter + r;
+      if (!this.strict) {
+        exact += "[" + this.delimiter + "]?";
+      }
+      match = path.match(new RegExp("^" + exact));
+      if (!match) {
+        continue;
+      }
+      if (match[0] && match[0] == path && routes[r][method]) {
+        next = [ [ routes[r].before, routes[r][method] ].filter(Boolean) ];
+        next.after = [ routes[r].after ].filter(Boolean);
+        next.matched = true;
+        next.captures = match.slice(1);
+        if (this.recurse && routes === this.routes) {
+          next.push([ routes.before, routes.on ].filter(Boolean));
+          next.after = next.after.concat([ routes.after ].filter(Boolean));
+        }
+        return filterRoutes(next);
+      }
+      next = this.traverse(method, path, routes[r], current);
+      if (next.matched) {
+        if (next.length > 0) {
+          fns = fns.concat(next);
+        }
+        if (this.recurse) {
+          fns.push([ routes[r].before, routes[r].on ].filter(Boolean));
+          next.after = next.after.concat([ routes[r].after ].filter(Boolean));
+          if (routes === this.routes) {
+            fns.push([ routes["before"], routes["on"] ].filter(Boolean));
+            next.after = next.after.concat([ routes["after"] ].filter(Boolean));
+          }
+        }
+        fns.matched = true;
+        fns.captures = next.captures;
+        fns.after = next.after;
+        return filterRoutes(fns);
+      }
+    }
+  }
+  return false;
+};
+
+Router.prototype.insert = function(method, path, route, parent) {
+  var methodType, parentType, isArray, nested, part;
+  path = path.filter(function(p) {
+    return p && p.length > 0;
+  });
+  parent = parent || this.routes;
+  part = path.shift();
+  if (/\:|\*/.test(part) && !/\\d|\\w/.test(part)) {
+    part = regifyString(part, this.params);
+  }
+  if (path.length > 0) {
+    parent[part] = parent[part] || {};
+    return this.insert(method, path, route, parent[part]);
+  }
+  if (!part && !path.length && parent === this.routes) {
+    methodType = typeof parent[method];
+    switch (methodType) {
+     case "function":
+      parent[method] = [ parent[method], route ];
+      return;
+     case "object":
+      parent[method].push(route);
+      return;
+     case "undefined":
+      parent[method] = route;
+      return;
+    }
+    return;
+  }
+  parentType = typeof parent[part];
+  isArray = Array.isArray(parent[part]);
+  if (parent[part] && !isArray && parentType == "object") {
+    methodType = typeof parent[part][method];
+    switch (methodType) {
+     case "function":
+      parent[part][method] = [ parent[part][method], route ];
+      return;
+     case "object":
+      parent[part][method].push(route);
+      return;
+     case "undefined":
+      parent[part][method] = route;
+      return;
+    }
+  } else if (parentType == "undefined") {
+    nested = {};
+    nested[method] = route;
+    parent[part] = nested;
+    return;
+  }
+  throw new Error("Invalid route context: " + parentType);
+};
+
+
+
+Router.prototype.extend = function(methods) {
+  var self = this, len = methods.length, i;
+  function extend(method) {
+    self._methods[method] = true;
+    self[method] = function() {
+      var extra = arguments.length === 1 ? [ method, "" ] : [ method ];
+      self.on.apply(self, extra.concat(Array.prototype.slice.call(arguments)));
+    };
+  }
+  for (i = 0; i < len; i++) {
+    extend(methods[i]);
+  }
+};
+
+Router.prototype.runlist = function(fns) {
+  var runlist = this.every && this.every.before ? [ this.every.before ].concat(_flatten(fns)) : _flatten(fns);
+  if (this.every && this.every.on) {
+    runlist.push(this.every.on);
+  }
+  runlist.captures = fns.captures;
+  runlist.source = fns.source;
+  return runlist;
+};
+
+Router.prototype.mount = function(routes, path) {
+  if (!routes || typeof routes !== "object" || Array.isArray(routes)) {
+    return;
+  }
+  var self = this;
+  path = path || [];
+  if (!Array.isArray(path)) {
+    path = path.split(self.delimiter);
+  }
+  function insertOrMount(route, local) {
+    var rename = route, parts = route.split(self.delimiter), routeType = typeof routes[route], isRoute = parts[0] === "" || !self._methods[parts[0]], event = isRoute ? "on" : rename;
+    if (isRoute) {
+      rename = rename.slice((rename.match(new RegExp("^" + self.delimiter)) || [ "" ])[0].length);
+      parts.shift();
+    }
+    if (isRoute && routeType === "object" && !Array.isArray(routes[route])) {
+      local = local.concat(parts);
+      self.mount(routes[route], local);
+      return;
+    }
+    if (isRoute) {
+      local = local.concat(rename.split(self.delimiter));
+      local = terminator(local, self.delimiter);
+    }
+    self.insert(event, local, routes[route]);
+  }
+  for (var route in routes) {
+    if (routes.hasOwnProperty(route)) {
+      insertOrMount(route, path.slice(0));
+    }
+  }
+};
+
+
+
+}(typeof exports === "object" ? exports : window));
\ No newline at end of file
diff --git a/examples/todomvc/node_modules/todomvc-app-css/index.css b/examples/todomvc/node_modules/todomvc-app-css/index.css
new file mode 100644
index 00000000000..ba79a58df33
--- /dev/null
+++ b/examples/todomvc/node_modules/todomvc-app-css/index.css
@@ -0,0 +1,378 @@
+html,
+body {
+	margin: 0;
+	padding: 0;
+}
+
+button {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	background: none;
+	font-size: 100%;
+	vertical-align: baseline;
+	font-family: inherit;
+	font-weight: inherit;
+	color: inherit;
+	-webkit-appearance: none;
+	appearance: none;
+	-webkit-font-smoothing: antialiased;
+	-moz-font-smoothing: antialiased;
+	font-smoothing: antialiased;
+}
+
+body {
+	font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
+	line-height: 1.4em;
+	background: #f5f5f5;
+	color: #4d4d4d;
+	min-width: 230px;
+	max-width: 550px;
+	margin: 0 auto;
+	-webkit-font-smoothing: antialiased;
+	-moz-font-smoothing: antialiased;
+	font-smoothing: antialiased;
+	font-weight: 300;
+}
+
+button,
+input[type="checkbox"] {
+	outline: none;
+}
+
+.hidden {
+	display: none;
+}
+
+.todoapp {
+	background: #fff;
+	margin: 130px 0 40px 0;
+	position: relative;
+	box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
+	            0 25px 50px 0 rgba(0, 0, 0, 0.1);
+}
+
+.todoapp input::-webkit-input-placeholder {
+	font-style: italic;
+	font-weight: 300;
+	color: #e6e6e6;
+}
+
+.todoapp input::-moz-placeholder {
+	font-style: italic;
+	font-weight: 300;
+	color: #e6e6e6;
+}
+
+.todoapp input::input-placeholder {
+	font-style: italic;
+	font-weight: 300;
+	color: #e6e6e6;
+}
+
+.todoapp h1 {
+	position: absolute;
+	top: -155px;
+	width: 100%;
+	font-size: 100px;
+	font-weight: 100;
+	text-align: center;
+	color: rgba(175, 47, 47, 0.15);
+	-webkit-text-rendering: optimizeLegibility;
+	-moz-text-rendering: optimizeLegibility;
+	text-rendering: optimizeLegibility;
+}
+
+.new-todo,
+.edit {
+	position: relative;
+	margin: 0;
+	width: 100%;
+	font-size: 24px;
+	font-family: inherit;
+	font-weight: inherit;
+	line-height: 1.4em;
+	border: 0;
+	outline: none;
+	color: inherit;
+	padding: 6px;
+	border: 1px solid #999;
+	box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
+	box-sizing: border-box;
+	-webkit-font-smoothing: antialiased;
+	-moz-font-smoothing: antialiased;
+	font-smoothing: antialiased;
+}
+
+.new-todo {
+	padding: 16px 16px 16px 60px;
+	border: none;
+	background: rgba(0, 0, 0, 0.003);
+	box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
+}
+
+.main {
+	position: relative;
+	z-index: 2;
+	border-top: 1px solid #e6e6e6;
+}
+
+label[for='toggle-all'] {
+	display: none;
+}
+
+.toggle-all {
+	position: absolute;
+	top: -55px;
+	left: -12px;
+	width: 60px;
+	height: 34px;
+	text-align: center;
+	border: none; /* Mobile Safari */
+}
+
+.toggle-all:before {
+	content: '❯';
+	font-size: 22px;
+	color: #e6e6e6;
+	padding: 10px 27px 10px 27px;
+}
+
+.toggle-all:checked:before {
+	color: #737373;
+}
+
+.todo-list {
+	margin: 0;
+	padding: 0;
+	list-style: none;
+}
+
+.todo-list li {
+	position: relative;
+	font-size: 24px;
+	border-bottom: 1px solid #ededed;
+}
+
+.todo-list li:last-child {
+	border-bottom: none;
+}
+
+.todo-list li.editing {
+	border-bottom: none;
+	padding: 0;
+}
+
+.todo-list li.editing .edit {
+	display: block;
+	width: 506px;
+	padding: 13px 17px 12px 17px;
+	margin: 0 0 0 43px;
+}
+
+.todo-list li.editing .view {
+	display: none;
+}
+
+.todo-list li .toggle {
+	text-align: center;
+	width: 40px;
+	/* auto, since non-WebKit browsers doesn't support input styling */
+	height: auto;
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	margin: auto 0;
+	border: none; /* Mobile Safari */
+	-webkit-appearance: none;
+	appearance: none;
+}
+
+.todo-list li .toggle:after {
+	content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>');
+}
+
+.todo-list li .toggle:checked:after {
+	content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>');
+}
+
+.todo-list li label {
+	white-space: pre;
+	word-break: break-word;
+	padding: 15px 60px 15px 15px;
+	margin-left: 45px;
+	display: block;
+	line-height: 1.2;
+	transition: color 0.4s;
+}
+
+.todo-list li.completed label {
+	color: #d9d9d9;
+	text-decoration: line-through;
+}
+
+.todo-list li .destroy {
+	display: none;
+	position: absolute;
+	top: 0;
+	right: 10px;
+	bottom: 0;
+	width: 40px;
+	height: 40px;
+	margin: auto 0;
+	font-size: 30px;
+	color: #cc9a9a;
+	margin-bottom: 11px;
+	transition: color 0.2s ease-out;
+}
+
+.todo-list li .destroy:hover {
+	color: #af5b5e;
+}
+
+.todo-list li .destroy:after {
+	content: '×';
+}
+
+.todo-list li:hover .destroy {
+	display: block;
+}
+
+.todo-list li .edit {
+	display: none;
+}
+
+.todo-list li.editing:last-child {
+	margin-bottom: -1px;
+}
+
+.footer {
+	color: #777;
+	padding: 10px 15px;
+	height: 20px;
+	text-align: center;
+	border-top: 1px solid #e6e6e6;
+}
+
+.footer:before {
+	content: '';
+	position: absolute;
+	right: 0;
+	bottom: 0;
+	left: 0;
+	height: 50px;
+	overflow: hidden;
+	box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
+	            0 8px 0 -3px #f6f6f6,
+	            0 9px 1px -3px rgba(0, 0, 0, 0.2),
+	            0 16px 0 -6px #f6f6f6,
+	            0 17px 2px -6px rgba(0, 0, 0, 0.2);
+}
+
+.todo-count {
+	float: left;
+	text-align: left;
+}
+
+.todo-count strong {
+	font-weight: 300;
+}
+
+.filters {
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	position: absolute;
+	right: 0;
+	left: 0;
+}
+
+.filters li {
+	display: inline;
+}
+
+.filters li a {
+	color: inherit;
+	margin: 3px;
+	padding: 3px 7px;
+	text-decoration: none;
+	border: 1px solid transparent;
+	border-radius: 3px;
+}
+
+.filters li a.selected,
+.filters li a:hover {
+	border-color: rgba(175, 47, 47, 0.1);
+}
+
+.filters li a.selected {
+	border-color: rgba(175, 47, 47, 0.2);
+}
+
+.clear-completed,
+html .clear-completed:active {
+	float: right;
+	position: relative;
+	line-height: 20px;
+	text-decoration: none;
+	cursor: pointer;
+	position: relative;
+}
+
+.clear-completed:hover {
+	text-decoration: underline;
+}
+
+.info {
+	margin: 65px auto 0;
+	color: #bfbfbf;
+	font-size: 10px;
+	text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+	text-align: center;
+}
+
+.info p {
+	line-height: 1;
+}
+
+.info a {
+	color: inherit;
+	text-decoration: none;
+	font-weight: 400;
+}
+
+.info a:hover {
+	text-decoration: underline;
+}
+
+/*
+	Hack to remove background from Mobile Safari.
+	Can't use it globally since it destroys checkboxes in Firefox
+*/
+@media screen and (-webkit-min-device-pixel-ratio:0) {
+	.toggle-all,
+	.todo-list li .toggle {
+		background: none;
+	}
+
+	.todo-list li .toggle {
+		height: 40px;
+	}
+
+	.toggle-all {
+		-webkit-transform: rotate(90deg);
+		transform: rotate(90deg);
+		-webkit-appearance: none;
+		appearance: none;
+	}
+}
+
+@media (max-width: 430px) {
+	.footer {
+		height: 50px;
+	}
+
+	.filters {
+		bottom: 10px;
+	}
+}
diff --git a/examples/todomvc/package.json b/examples/todomvc/package.json
new file mode 100644
index 00000000000..c90a3d35035
--- /dev/null
+++ b/examples/todomvc/package.json
@@ -0,0 +1,9 @@
+{
+  "private": true,
+  "dependencies": {
+    "director": "^1.2.0",
+    "vue": "^1.0.0",
+    "todomvc-common": "^1.0.1",
+    "todomvc-app-css": "^2.0.0"
+  }
+}
diff --git a/examples/todomvc/perf.js b/examples/todomvc/perf.js
new file mode 100644
index 00000000000..f6cd456a331
--- /dev/null
+++ b/examples/todomvc/perf.js
@@ -0,0 +1,78 @@
+setTimeout(function () {
+
+  if (window.isPhantom) return
+  
+  // Initial load & render metrics
+
+  metrics.afterRenderAsync = now()
+  console.log('Vue load     : ' + (metrics.afterLoad - metrics.beforeLoad).toFixed(2) + 'ms')
+  console.log('Render sync  : ' + (metrics.afterRender - metrics.beforeRender).toFixed(2) + 'ms')
+  console.log('Render async : ' + (metrics.afterRenderAsync - metrics.beforeRender).toFixed(2) + 'ms')
+  console.log('Total sync   : ' + (metrics.afterRender - metrics.beforeLoad).toFixed(2) + 'ms')
+  console.log('Total async  : ' + (metrics.afterRenderAsync - metrics.beforeLoad).toFixed(2) + 'ms')
+
+  // Benchmark
+  // add 100 items
+  // toggle them one by one
+  // then delete them one by one
+
+  var benchSetting = window.location.search.match(/\bbenchmark=(\d+)/)
+  if (!benchSetting) return
+
+  var itemsToAdd = +benchSetting[1],
+    render,
+    bench,
+    addTime,
+    toggleTime,
+    removeTime
+
+  var start = now(),
+    last
+
+  add()
+
+  function add() {
+    last = now()
+    var newTodo = '12345',
+      todoInput = document.getElementById('new-todo')
+    for (var i = 0; i < itemsToAdd; i++) {
+      var keyupEvent = document.createEvent('Event');
+      keyupEvent.initEvent('keyup', true, true);
+      keyupEvent.keyCode = 13;
+      app.newTodo = 'Something to do ' + i;
+      todoInput.dispatchEvent(keyupEvent)
+    }
+    setTimeout(toggle, 0)
+  }
+
+  function toggle () {
+    addTime = now() - last
+    var checkboxes = document.querySelectorAll('.toggle')
+    for (var i = 0; i < checkboxes.length; i++) {
+      checkboxes[i].click()
+    }
+    last = now()
+    setTimeout(remove, 0)
+  }
+
+  function remove () {
+    toggleTime = now() - last
+    var deleteButtons = document.querySelectorAll('.destroy');
+    for (var i = 0; i < deleteButtons.length; i++) {
+      deleteButtons[i].click()
+    }
+    last = now()
+    setTimeout(report, 0)
+  }
+
+  function report () {
+    bench = now() - start
+    removeTime = now() - last
+    console.log('\nBenchmark x ' + itemsToAdd)
+    console.log('add    : ' + addTime.toFixed(2) + 'ms')
+    console.log('toggle : ' + toggleTime.toFixed(2) + 'ms')
+    console.log('remove : ' + removeTime.toFixed(2) + 'ms')
+    console.log('total  : ' + bench.toFixed(2) + 'ms')
+  }
+
+}, 0)
diff --git a/examples/classic/todomvc/readme.md b/examples/todomvc/readme.md
similarity index 54%
rename from examples/classic/todomvc/readme.md
rename to examples/todomvc/readme.md
index db6affe8af7..9a4f2513e7b 100644
--- a/examples/classic/todomvc/readme.md
+++ b/examples/todomvc/readme.md
@@ -3,20 +3,21 @@
 > Vue.js is a library for building interactive web interfaces. 
 It provides data-driven, nestable view components with a simple and flexible API.
 
-> _[Vue.js - v2.vuejs.org](https://v2.vuejs.org)_
+> _[Vue.js - vuejs.org](http://vuejs.org)_
 
 ## Learning Vue.js
-The [Vue.js website](https://v2.vuejs.org/) is a great resource to get started.
+The [Vue.js website](http://vuejs.org/) is a great resource to get started.
 
 Here are some links you may find helpful:
 
-* [Official Guide](https://v2.vuejs.org/guide/)
-* [API Reference](https://v2.vuejs.org/api/)
-* [Examples](https://v2.vuejs.org/examples/)
+* [Official Guide](http://vuejs.org/guide/)
+* [API Reference](http://vuejs.org/api/)
+* [Examples](http://vuejs.org/examples/)
+* [Building Larger Apps with Vue.js](http://vuejs.org/guide/application.html)
 
 Get help from other Vue.js users:
 
-* [Vue.js official forum](https://forum.vuejs.org)
+* [Vue.js official forum](http://forum.vuejs.org)
 * [Vue.js on Twitter](https://twitter.com/vuejs)
 * [Vue.js on Gitter](https://gitter.im/vuejs/vue)
 
@@ -24,4 +25,4 @@ _If you have other helpful links to share, or find any of the links above no lon
 
 ## Credit
 
-This TodoMVC application was created by [Evan You](https://evanyou.me).
+This TodoMVC application was created by [Evan You](http://evanyou.me).
diff --git a/examples/classic/tree/index.html b/examples/tree/index.html
similarity index 85%
rename from examples/classic/tree/index.html
rename to examples/tree/index.html
index a32ff272f75..32aad56c3d7 100644
--- a/examples/classic/tree/index.html
+++ b/examples/tree/index.html
@@ -2,7 +2,7 @@
 <html lang="en">
   <head>
     <meta charset="utf-8">
-    <title>Vue.js tree view example</title>
+    <title>Vue.js tree-view demo</title>
     <style>
       body {
         font-family: Menlo, Consolas, monospace;
@@ -20,8 +20,7 @@
         list-style-type: dot;
       }
     </style>
-    <!-- Delete ".min" for console warnings in development -->
-    <script src="../../../dist/vue.min.js"></script>
+    <script src="../../dist/vue.js"></script>
   </head>
   <body>
 
@@ -41,7 +40,7 @@
             v-for="model in model.children"
             :model="model">
           </item>
-          <li class="add" @click="addChild">+</li>
+          <li @click="addChild">+</li>
         </ul>
       </li>
     </script>
diff --git a/examples/classic/tree/tree.js b/examples/tree/tree.js
similarity index 98%
rename from examples/classic/tree/tree.js
rename to examples/tree/tree.js
index eb66c581554..05222fbece6 100644
--- a/examples/classic/tree/tree.js
+++ b/examples/tree/tree.js
@@ -31,6 +31,7 @@ var data = {
 // define the item component
 Vue.component('item', {
   template: '#item-template',
+  replace: true,
   props: {
     model: Object
   },
diff --git a/issue_template.md b/issue_template.md
new file mode 100644
index 00000000000..6eb8209ea82
--- /dev/null
+++ b/issue_template.md
@@ -0,0 +1,46 @@
+<!--
+Thank you for contributing! Please carefully read the following before opening your issue.
+
+中文用户:请尽量用英文描述你的 issue,这样能够让尽可能多的人帮到你。
+
+Got a question?
+===============
+The issue list of this repo is **exclusively** for bug reports and feature requests. For simple questions, please use the following resources:
+
+- Read the docs: http://vuejs.org/guide/
+- Watch video tutorials: https://laracasts.com/series/learning-vue-step-by-step
+- Ask in the Gitter chat room: https://gitter.im/vuejs/vue
+- Ask on the forums: http://forum.vuejs.org/
+- Look for/ask questions on stack overflow: https://stackoverflow.com/questions/ask?tags=vue.js
+
+Reporting a bug?
+================
+- Try to search for your issue, it may have already been answered or even fixed in the development branch.
+
+- Check if the issue is reproducible with the latest stable version of Vue. If you are using a pre-release, please indicate the specific version you are using.
+
+- It is **required** that you clearly describe the steps necessary to reproduce the issue you are running into. Issues with no clear repro steps will not be triaged. If an issue labeled "need repro" receives no further input from the issue author for more than 5 days, it will be closed.
+
+- It is recommended that you make a JSFiddle/JSBin/Codepen to demonstrate your issue. You could start with [this template](http://jsfiddle.net/5sH6A/) that already includes the latest version of Vue.
+
+- For bugs that involves build setups, you can create a reproduction repository with steps in the README.
+
+- If your issue is resolved but still open, don’t hesitate to close it. In case you found a solution by yourself, it could be helpful to explain how you fixed it.
+
+Have a feature request?
+=======================
+Remove the template from below and provide thoughtful commentary *and code samples* on what this feature means for your product. What will it allow you to do that you can't do today? How will it make current work-arounds straightforward? What potential bugs and edge cases does it help to avoid? etc. Please keep it product-centric.
+-->
+
+<!-- BUG REPORT TEMPLATE -->
+### Vue.js version
+1.0.23
+
+### Reproduction Link
+<!-- A minimal JSBin, JSFiddle, Codepen, or a GitHub repository that can reproduce the bug. -->
+
+### Steps to reproduce
+
+### What is Expected?
+
+### What is actually happening?
diff --git a/package.json b/package.json
index 52308153a89..3973cc0e088 100644
--- a/package.json
+++ b/package.json
@@ -1,136 +1,83 @@
 {
   "name": "vue",
-  "version": "2.7.16",
-  "packageManager": "pnpm@8.9.2",
-  "description": "Reactive, component-oriented view layer for modern web interfaces.",
-  "main": "dist/vue.runtime.common.js",
-  "module": "dist/vue.runtime.esm.js",
-  "unpkg": "dist/vue.js",
-  "jsdelivr": "dist/vue.js",
-  "typings": "types/index.d.ts",
+  "version": "1.0.28",
+  "author": "Evan You <yyx990803@gmail.com>",
+  "license": "MIT",
+  "description": "Simple, Fast & Composable MVVM for building interactive interfaces",
+  "keywords": [
+    "mvvm",
+    "browser",
+    "framework"
+  ],
+  "main": "dist/vue.common.js",
   "files": [
-    "src",
-    "dist/*.js",
-    "dist/*.mjs",
-    "types/*.d.ts",
-    "compiler-sfc",
-    "packages/compiler-sfc"
+    "dist/vue.js",
+    "dist/vue.min.js",
+    "dist/vue.min.js.map",
+    "dist/vue.common.js",
+    "src"
   ],
-  "exports": {
-    ".": {
-      "types": "./types/index.d.ts",
-      "import": {
-        "node": "./dist/vue.runtime.mjs",
-        "default": "./dist/vue.runtime.esm.js"
-      },
-      "require": "./dist/vue.runtime.common.js"
-    },
-    "./compiler-sfc": {
-      "types": "./compiler-sfc/index.d.ts",
-      "import": "./compiler-sfc/index.mjs",
-      "require": "./compiler-sfc/index.js"
-    },
-    "./dist/*": "./dist/*",
-    "./types/*": [
-      "./types/*.d.ts",
-      "./types/*"
-    ],
-    "./package.json": "./package.json"
-  },
-  "sideEffects": false,
-  "scripts": {
-    "dev": "rollup -w -c scripts/config.js --environment TARGET:full-dev",
-    "dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:runtime-cjs-dev",
-    "dev:esm": "rollup -w -c scripts/config.js --environment TARGET:runtime-esm",
-    "dev:ssr": "rollup -w -c scripts/config.js --environment TARGET:server-renderer",
-    "dev:compiler": "rollup -w -c scripts/config.js --environment TARGET:compiler ",
-    "build": "node scripts/build.js",
-    "build:ssr": "npm run build -- runtime-cjs,server-renderer",
-    "build:types": "rimraf temp && tsc --declaration --emitDeclarationOnly --outDir temp && api-extractor run && api-extractor run -c packages/compiler-sfc/api-extractor.json",
-    "test": "npm run ts-check && npm run test:types && npm run test:unit && npm run test:e2e && npm run test:ssr && npm run test:sfc",
-    "test:unit": "vitest run test/unit",
-    "test:ssr": "npm run build:ssr && vitest run server-renderer",
-    "test:sfc": "vitest run compiler-sfc",
-    "test:e2e": "npm run build -- full-prod,server-renderer-basic && vitest run test/e2e",
-    "test:transition": "karma start test/transition/karma.conf.js",
-    "test:types": "npm run build:types && tsc -p ./types/tsconfig.json",
-    "format": "prettier --write --parser typescript \"(src|test|packages|types)/**/*.ts\"",
-    "ts-check": "tsc -p tsconfig.json --noEmit",
-    "ts-check:test": "tsc -p test/tsconfig.json --noEmit",
-    "bench:ssr": "npm run build:ssr && node benchmarks/ssr/renderToString.js && node benchmarks/ssr/renderToStream.js",
-    "release": "node scripts/release.js",
-    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
-  },
-  "gitHooks": {
-    "pre-commit": "lint-staged",
-    "commit-msg": "node scripts/verify-commit-msg.js"
-  },
-  "lint-staged": {
-    "*.js": [
-      "prettier --write"
-    ],
-    "*.ts": [
-      "prettier --parser=typescript --write"
-    ]
-  },
   "repository": {
     "type": "git",
-    "url": "git+https://github.com/vuejs/vue.git"
+    "url": "https://github.com/vuejs/vue.git"
   },
-  "keywords": [
-    "vue"
-  ],
-  "author": "Evan You",
-  "license": "MIT",
-  "bugs": {
-    "url": "https://github.com/vuejs/vue/issues"
+  "bugs": "https://github.com/vuejs/vue/issues",
+  "homepage": "http://vuejs.org",
+  "scripts": {
+    "test": "npm run lint && npm run cover && npm run build && npm run e2e && npm run types",
+    "build": "node build/build.js",
+    "install-hook": "ln -s ../../build/git-hooks/pre-commit .git/hooks/pre-commit",
+    "dev": "webpack --watch --config build/webpack.dev.config.js & npm run serve-test",
+    "serve-test": "webpack-dev-server --config build/webpack.test.config.js --host 0.0.0.0",
+    "build-test": "webpack --config build/webpack.test.config.js",
+    "lint": "eslint src test/e2e test/unit/specs build",
+    "e2e": "casperjs test --concise ./test/e2e",
+    "unit": "karma start build/karma.unit.config.js",
+    "cover": "karma start build/karma.cover.config.js",
+    "types": "tsc -p ./types/test/tsconfig.json",
+    "sauce": "karma start build/karma.sauce.config.js",
+    "sauce-all": "npm run sauce && npm run sauce -- 1 && npm run sauce -- 2",
+    "release": "bash build/release.sh",
+    "release-csp": "bash build/release-csp.sh"
   },
-  "homepage": "https://github.com/vuejs/vue#readme",
   "dependencies": {
-    "@vue/compiler-sfc": "workspace:*",
-    "csstype": "^3.1.0"
+    "envify": "^3.4.0"
+  },
+  "browserify": {
+    "transform": [
+      "envify"
+    ]
   },
   "devDependencies": {
-    "@babel/parser": "^7.23.5",
-    "@microsoft/api-extractor": "^7.25.0",
-    "@rollup/plugin-alias": "^3.1.9",
-    "@rollup/plugin-commonjs": "^22.0.0",
-    "@rollup/plugin-node-resolve": "^13.3.0",
-    "@rollup/plugin-replace": "^4.0.0",
-    "@types/he": "^1.1.2",
-    "@types/node": "^20.10.3",
-    "chalk": "^4.1.2",
-    "conventional-changelog-cli": "^2.2.2",
-    "cross-spawn": "^7.0.3",
-    "enquirer": "^2.3.6",
-    "esbuild": "^0.19.8",
-    "execa": "^4.1.0",
-    "he": "^1.2.0",
-    "jasmine-core": "^4.2.0",
-    "jsdom": "^19.0.0",
-    "karma": "^6.3.20",
-    "karma-chrome-launcher": "^3.1.1",
-    "karma-cli": "^2.0.0",
-    "karma-esbuild": "^2.2.5",
-    "karma-jasmine": "^5.0.1",
-    "lint-staged": "^12.5.0",
-    "lodash": "^4.17.21",
-    "marked": "^4.0.16",
-    "minimist": "^1.2.6",
-    "postcss": "^8.4.14",
-    "prettier": "^2.6.2",
-    "puppeteer": "^14.3.0",
-    "rimraf": "^3.0.2",
-    "rollup": "^2.79.1",
-    "rollup-plugin-typescript2": "^0.32.0",
-    "semver": "^7.3.7",
-    "shelljs": "^0.8.5",
-    "terser": "^5.14.0",
-    "todomvc-app-css": "^2.4.2",
-    "ts-node": "^10.8.1",
-    "tslib": "^2.4.0",
-    "typescript": "^4.8.4",
-    "vitest": "^1.0.4",
-    "yorkie": "^2.0.0"
+    "babel-core": "^5.8.0",
+    "babel-loader": "^5.4.0",
+    "babel-runtime": "^5.8.0",
+    "casperjs": "^1.1.3",
+    "codecov.io": "^0.1.2",
+    "eslint": "^3.5.0",
+    "eslint-config-vue": "^1.0.0",
+    "eslint-plugin-html": "^1.5.2",
+    "istanbul-instrumenter-loader": "^0.2.0",
+    "jasmine-core": "^2.4.1",
+    "karma": "^1.3.0",
+    "karma-chrome-launcher": "^2.0.0",
+    "karma-coverage": "^1.1.1",
+    "karma-firefox-launcher": "^1.0.0",
+    "karma-ie-launcher": "^1.0.0",
+    "karma-jasmine": "^1.0.2",
+    "karma-phantomjs-launcher": "^1.0.2",
+    "karma-safari-launcher": "^1.0.0",
+    "karma-sauce-launcher": "^1.0.0",
+    "karma-sourcemap-loader": "^0.3.7",
+    "karma-webpack": "^1.7.0",
+    "object-assign": "^4.0.1",
+    "phantomjs": "^1.9.17",
+    "rollup": "^0.34.13",
+    "rollup-plugin-babel": "^1.0.0",
+    "rollup-plugin-replace": "^1.1.0",
+    "typescript": "^2.1.5",
+    "uglify-js": "^2.4.24",
+    "webpack": "^1.12.2",
+    "webpack-dev-server": "^1.12.1"
   }
 }
diff --git a/packages/compiler-sfc/api-extractor.json b/packages/compiler-sfc/api-extractor.json
deleted file mode 100644
index eda03ee2119..00000000000
--- a/packages/compiler-sfc/api-extractor.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
-  "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
-
-  "projectFolder": ".",
-
-  "mainEntryPointFilePath": "../../temp/packages/compiler-sfc/src/index.d.ts",
-
-  "compiler": {
-    "tsconfigFilePath": "../../api-extractor.tsconfig.json"
-  },
-
-  "dtsRollup": {
-    "enabled": true,
-    "untrimmedFilePath": "",
-    "publicTrimmedFilePath": "./dist/compiler-sfc.d.ts"
-  },
-
-  "apiReport": {
-    "enabled": false
-  },
-
-  "docModel": {
-    "enabled": false
-  },
-
-  "tsdocMetadata": {
-    "enabled": false
-  },
-
-  "messages": {
-    "compilerMessageReporting": {
-      "default": {
-        "logLevel": "warning"
-      }
-    },
-
-    "extractorMessageReporting": {
-      "default": {
-        "logLevel": "warning",
-        "addToApiReportFile": true
-      },
-
-      "ae-missing-release-tag": {
-        "logLevel": "none"
-      },
-      "ae-internal-missing-underscore": {
-        "logLevel": "none"
-      },
-      "ae-forgotten-export": {
-        "logLevel": "none"
-      }
-    },
-
-    "tsdocMessageReporting": {
-      "default": {
-        "logLevel": "warning"
-      },
-
-      "tsdoc-undefined-tag": {
-        "logLevel": "none"
-      }
-    }
-  }
-}
diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json
deleted file mode 100644
index d53b029c14f..00000000000
--- a/packages/compiler-sfc/package.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
-  "name": "@vue/compiler-sfc",
-  "version": "2.7.16",
-  "description": "compiler-sfc for Vue 2",
-  "main": "dist/compiler-sfc.js",
-  "types": "dist/compiler-sfc.d.ts",
-  "files": [
-    "dist"
-  ],
-  "dependencies": {
-    "@babel/parser": "^7.23.5",
-    "postcss": "^8.4.14",
-    "source-map": "^0.6.1"
-  },
-  "devDependencies": {
-    "@babel/types": "^7.23.5",
-    "@types/estree": "^0.0.48",
-    "@types/hash-sum": "^1.0.0",
-    "@types/lru-cache": "^5.1.1",
-    "@vue/consolidate": "^0.17.3",
-    "de-indent": "^1.0.2",
-    "estree-walker": "^2.0.2",
-    "hash-sum": "^2.0.0",
-    "less": "^4.1.3",
-    "lru-cache": "^5.1.1",
-    "magic-string": "^0.25.9",
-    "merge-source-map": "^1.1.0",
-    "postcss-modules": "^4.3.1",
-    "postcss-selector-parser": "^6.0.10",
-    "pug": "^3.0.2",
-    "sass": "^1.52.3",
-    "stylus": "^0.58.1"
-  },
-  "optionalDependencies": {
-    "prettier": "^1.18.2 || ^2.0.0"
-  }
-}
diff --git a/packages/compiler-sfc/src/babelUtils.ts b/packages/compiler-sfc/src/babelUtils.ts
deleted file mode 100644
index 306cb8077a7..00000000000
--- a/packages/compiler-sfc/src/babelUtils.ts
+++ /dev/null
@@ -1,423 +0,0 @@
-// https://github.com/vuejs/core/blob/main/packages/compiler-core/src/babelUtils.ts
-
-// should only use types from @babel/types
-// do not import runtime methods
-import type {
-  Identifier,
-  Node,
-  Function,
-  ObjectProperty,
-  BlockStatement,
-  Program
-} from '@babel/types'
-import { walk } from 'estree-walker'
-
-export function walkIdentifiers(
-  root: Node,
-  onIdentifier: (
-    node: Identifier,
-    parent: Node,
-    parentStack: Node[],
-    isReference: boolean,
-    isLocal: boolean
-  ) => void,
-  onNode?: (node: Node) => void
-) {
-  const includeAll = false
-  const parentStack: Node[] = []
-  const knownIds: Record<string, number> = Object.create(null)
-
-  const rootExp =
-    root.type === 'Program' &&
-    root.body[0].type === 'ExpressionStatement' &&
-    root.body[0].expression
-
-  ;(walk as any)(root, {
-    enter(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
-      parent && parentStack.push(parent)
-      if (
-        parent &&
-        parent.type.startsWith('TS') &&
-        parent.type !== 'TSAsExpression' &&
-        parent.type !== 'TSNonNullExpression' &&
-        parent.type !== 'TSTypeAssertion'
-      ) {
-        return this.skip()
-      }
-
-      if (onNode) onNode(node)
-
-      if (node.type === 'Identifier') {
-        const isLocal = !!knownIds[node.name]
-        const isRefed = isReferencedIdentifier(node, parent!, parentStack)
-        if (includeAll || (isRefed && !isLocal)) {
-          onIdentifier(node, parent!, parentStack, isRefed, isLocal)
-        }
-      } else if (
-        node.type === 'ObjectProperty' &&
-        parent!.type === 'ObjectPattern'
-      ) {
-        // mark property in destructure pattern
-        ;(node as any).inPattern = true
-      } else if (isFunctionType(node)) {
-        // walk function expressions and add its arguments to known identifiers
-        // so that we don't prefix them
-        walkFunctionParams(node, id => markScopeIdentifier(node, id, knownIds))
-      } else if (node.type === 'BlockStatement') {
-        // #3445 record block-level local variables
-        walkBlockDeclarations(node, id =>
-          markScopeIdentifier(node, id, knownIds)
-        )
-      }
-    },
-    leave(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
-      parent && parentStack.pop()
-      if (node !== rootExp && node.scopeIds) {
-        for (const id of node.scopeIds) {
-          knownIds[id]--
-          if (knownIds[id] === 0) {
-            delete knownIds[id]
-          }
-        }
-      }
-    }
-  })
-}
-
-export function isReferencedIdentifier(
-  id: Identifier,
-  parent: Node | null,
-  parentStack: Node[]
-) {
-  if (!parent) {
-    return true
-  }
-
-  // is a special keyword but parsed as identifier
-  if (id.name === 'arguments') {
-    return false
-  }
-
-  if (isReferenced(id, parent)) {
-    return true
-  }
-
-  // babel's isReferenced check returns false for ids being assigned to, so we
-  // need to cover those cases here
-  switch (parent.type) {
-    case 'AssignmentExpression':
-    case 'AssignmentPattern':
-      return true
-    case 'ObjectPattern':
-    case 'ArrayPattern':
-      return isInDestructureAssignment(parent, parentStack)
-  }
-
-  return false
-}
-
-export function isInDestructureAssignment(
-  parent: Node,
-  parentStack: Node[]
-): boolean {
-  if (
-    parent &&
-    (parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
-  ) {
-    let i = parentStack.length
-    while (i--) {
-      const p = parentStack[i]
-      if (p.type === 'AssignmentExpression') {
-        return true
-      } else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
-        break
-      }
-    }
-  }
-  return false
-}
-
-export function walkFunctionParams(
-  node: Function,
-  onIdent: (id: Identifier) => void
-) {
-  for (const p of node.params) {
-    for (const id of extractIdentifiers(p)) {
-      onIdent(id)
-    }
-  }
-}
-
-export function walkBlockDeclarations(
-  block: BlockStatement | Program,
-  onIdent: (node: Identifier) => void
-) {
-  for (const stmt of block.body) {
-    if (stmt.type === 'VariableDeclaration') {
-      if (stmt.declare) continue
-      for (const decl of stmt.declarations) {
-        for (const id of extractIdentifiers(decl.id)) {
-          onIdent(id)
-        }
-      }
-    } else if (
-      stmt.type === 'FunctionDeclaration' ||
-      stmt.type === 'ClassDeclaration'
-    ) {
-      if (stmt.declare || !stmt.id) continue
-      onIdent(stmt.id)
-    }
-  }
-}
-
-export function extractIdentifiers(
-  param: Node,
-  nodes: Identifier[] = []
-): Identifier[] {
-  switch (param.type) {
-    case 'Identifier':
-      nodes.push(param)
-      break
-
-    case 'MemberExpression':
-      let object: any = param
-      while (object.type === 'MemberExpression') {
-        object = object.object
-      }
-      nodes.push(object)
-      break
-
-    case 'ObjectPattern':
-      for (const prop of param.properties) {
-        if (prop.type === 'RestElement') {
-          extractIdentifiers(prop.argument, nodes)
-        } else {
-          extractIdentifiers(prop.value, nodes)
-        }
-      }
-      break
-
-    case 'ArrayPattern':
-      param.elements.forEach(element => {
-        if (element) extractIdentifiers(element, nodes)
-      })
-      break
-
-    case 'RestElement':
-      extractIdentifiers(param.argument, nodes)
-      break
-
-    case 'AssignmentPattern':
-      extractIdentifiers(param.left, nodes)
-      break
-  }
-
-  return nodes
-}
-
-function markScopeIdentifier(
-  node: Node & { scopeIds?: Set<string> },
-  child: Identifier,
-  knownIds: Record<string, number>
-) {
-  const { name } = child
-  if (node.scopeIds && node.scopeIds.has(name)) {
-    return
-  }
-  if (name in knownIds) {
-    knownIds[name]++
-  } else {
-    knownIds[name] = 1
-  }
-  ;(node.scopeIds || (node.scopeIds = new Set())).add(name)
-}
-
-export const isFunctionType = (node: Node): node is Function => {
-  return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
-}
-
-export const isStaticProperty = (node: Node): node is ObjectProperty =>
-  node &&
-  (node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
-  !node.computed
-
-export const isStaticPropertyKey = (node: Node, parent: Node) =>
-  isStaticProperty(parent) && parent.key === node
-
-/**
- * Copied from https://github.com/babel/babel/blob/main/packages/babel-types/src/validators/isReferenced.ts
- * To avoid runtime dependency on @babel/types (which includes process references)
- * This file should not change very often in babel but we may need to keep it
- * up-to-date from time to time.
- *
- * https://github.com/babel/babel/blob/main/LICENSE
- *
- */
-function isReferenced(node: Node, parent: Node, grandparent?: Node): boolean {
-  switch (parent.type) {
-    // yes: PARENT[NODE]
-    // yes: NODE.child
-    // no: parent.NODE
-    case 'MemberExpression':
-    case 'OptionalMemberExpression':
-      if (parent.property === node) {
-        return !!parent.computed
-      }
-      return parent.object === node
-
-    case 'JSXMemberExpression':
-      return parent.object === node
-    // no: let NODE = init;
-    // yes: let id = NODE;
-    case 'VariableDeclarator':
-      return parent.init === node
-
-    // yes: () => NODE
-    // no: (NODE) => {}
-    case 'ArrowFunctionExpression':
-      return parent.body === node
-
-    // no: class { #NODE; }
-    // no: class { get #NODE() {} }
-    // no: class { #NODE() {} }
-    // no: class { fn() { return this.#NODE; } }
-    case 'PrivateName':
-      return false
-
-    // no: class { NODE() {} }
-    // yes: class { [NODE]() {} }
-    // no: class { foo(NODE) {} }
-    case 'ClassMethod':
-    case 'ClassPrivateMethod':
-    case 'ObjectMethod':
-      if (parent.key === node) {
-        return !!parent.computed
-      }
-      return false
-
-    // yes: { [NODE]: "" }
-    // no: { NODE: "" }
-    // depends: { NODE }
-    // depends: { key: NODE }
-    case 'ObjectProperty':
-      if (parent.key === node) {
-        return !!parent.computed
-      }
-      // parent.value === node
-      return !grandparent || grandparent.type !== 'ObjectPattern'
-    // no: class { NODE = value; }
-    // yes: class { [NODE] = value; }
-    // yes: class { key = NODE; }
-    case 'ClassProperty':
-      if (parent.key === node) {
-        return !!parent.computed
-      }
-      return true
-    case 'ClassPrivateProperty':
-      return parent.key !== node
-
-    // no: class NODE {}
-    // yes: class Foo extends NODE {}
-    case 'ClassDeclaration':
-    case 'ClassExpression':
-      return parent.superClass === node
-
-    // yes: left = NODE;
-    // no: NODE = right;
-    case 'AssignmentExpression':
-      return parent.right === node
-
-    // no: [NODE = foo] = [];
-    // yes: [foo = NODE] = [];
-    case 'AssignmentPattern':
-      return parent.right === node
-
-    // no: NODE: for (;;) {}
-    case 'LabeledStatement':
-      return false
-
-    // no: try {} catch (NODE) {}
-    case 'CatchClause':
-      return false
-
-    // no: function foo(...NODE) {}
-    case 'RestElement':
-      return false
-
-    case 'BreakStatement':
-    case 'ContinueStatement':
-      return false
-
-    // no: function NODE() {}
-    // no: function foo(NODE) {}
-    case 'FunctionDeclaration':
-    case 'FunctionExpression':
-      return false
-
-    // no: export NODE from "foo";
-    // no: export * as NODE from "foo";
-    case 'ExportNamespaceSpecifier':
-    case 'ExportDefaultSpecifier':
-      return false
-
-    // no: export { foo as NODE };
-    // yes: export { NODE as foo };
-    // no: export { NODE as foo } from "foo";
-    case 'ExportSpecifier':
-      // @ts-expect-error
-      if (grandparent?.source) {
-        return false
-      }
-      return parent.local === node
-
-    // no: import NODE from "foo";
-    // no: import * as NODE from "foo";
-    // no: import { NODE as foo } from "foo";
-    // no: import { foo as NODE } from "foo";
-    // no: import NODE from "bar";
-    case 'ImportDefaultSpecifier':
-    case 'ImportNamespaceSpecifier':
-    case 'ImportSpecifier':
-      return false
-
-    // no: import "foo" assert { NODE: "json" }
-    case 'ImportAttribute':
-      return false
-
-    // no: <div NODE="foo" />
-    case 'JSXAttribute':
-      return false
-
-    // no: [NODE] = [];
-    // no: ({ NODE }) = [];
-    case 'ObjectPattern':
-    case 'ArrayPattern':
-      return false
-
-    // no: new.NODE
-    // no: NODE.target
-    case 'MetaProperty':
-      return false
-
-    // yes: type X = { someProperty: NODE }
-    // no: type X = { NODE: OtherType }
-    case 'ObjectTypeProperty':
-      return parent.key !== node
-
-    // yes: enum X { Foo = NODE }
-    // no: enum X { NODE }
-    case 'TSEnumMember':
-      return parent.id !== node
-
-    // yes: { [NODE]: value }
-    // no: { NODE: value }
-    case 'TSPropertySignature':
-      if (parent.key === node) {
-        return !!parent.computed
-      }
-
-      return true
-  }
-
-  return true
-}
diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts
deleted file mode 100644
index 275945a2bfa..00000000000
--- a/packages/compiler-sfc/src/compileScript.ts
+++ /dev/null
@@ -1,1916 +0,0 @@
-import MagicString from 'magic-string'
-import LRU from 'lru-cache'
-import { walkIdentifiers, isFunctionType } from './babelUtils'
-import { BindingMetadata, BindingTypes } from './types'
-import { SFCDescriptor, SFCScriptBlock } from './parseComponent'
-import {
-  parse as _parse,
-  parseExpression,
-  ParserOptions,
-  ParserPlugin
-} from '@babel/parser'
-import { generateCodeFrame } from 'compiler/codeframe'
-import { camelize, capitalize, isBuiltInTag, makeMap } from 'shared/util'
-import { parseHTML } from 'compiler/parser/html-parser'
-import { baseOptions as webCompilerOptions } from 'web/compiler/options'
-import {
-  Node,
-  Declaration,
-  ObjectPattern,
-  ObjectExpression,
-  ArrayPattern,
-  Identifier,
-  ExportSpecifier,
-  TSType,
-  TSTypeLiteral,
-  TSFunctionType,
-  ObjectProperty,
-  ArrayExpression,
-  Statement,
-  CallExpression,
-  RestElement,
-  TSInterfaceBody,
-  Program,
-  ObjectMethod,
-  LVal,
-  Expression
-} from '@babel/types'
-import { walk } from 'estree-walker'
-import { RawSourceMap } from 'source-map'
-import { warnOnce } from './warn'
-import { isReservedTag } from 'web/util'
-import { bindRE, dirRE, onRE, slotRE } from 'compiler/parser'
-import { parseText } from 'compiler/parser/text-parser'
-import { DEFAULT_FILENAME } from './parseComponent'
-import {
-  CSS_VARS_HELPER,
-  genCssVarsCode,
-  genNormalScriptCssVarsCode
-} from './cssVars'
-import { rewriteDefault } from './rewriteDefault'
-
-// Special compiler macros
-const DEFINE_PROPS = 'defineProps'
-const DEFINE_EMITS = 'defineEmits'
-const DEFINE_EXPOSE = 'defineExpose'
-const WITH_DEFAULTS = 'withDefaults'
-
-// constants
-const DEFAULT_VAR = `__default__`
-
-const isBuiltInDir = makeMap(
-  `once,memo,if,for,else,else-if,slot,text,html,on,bind,model,show,cloak,is`
-)
-
-export interface SFCScriptCompileOptions {
-  /**
-   * Scope ID for prefixing injected CSS variables.
-   * This must be consistent with the `id` passed to `compileStyle`.
-   */
-  id: string
-  /**
-   * Production mode. Used to determine whether to generate hashed CSS variables
-   */
-  isProd?: boolean
-  /**
-   * Enable/disable source map. Defaults to true.
-   */
-  sourceMap?: boolean
-  /**
-   * https://babeljs.io/docs/en/babel-parser#plugins
-   */
-  babelParserPlugins?: ParserPlugin[]
-}
-
-export interface ImportBinding {
-  isType: boolean
-  imported: string
-  source: string
-  isFromSetup: boolean
-  isUsedInTemplate: boolean
-}
-
-/**
- * Compile `<script setup>`
- * It requires the whole SFC descriptor because we need to handle and merge
- * normal `<script>` + `<script setup>` if both are present.
- */
-export function compileScript(
-  sfc: SFCDescriptor,
-  options: SFCScriptCompileOptions = { id: '' }
-): SFCScriptBlock {
-  let { filename, script, scriptSetup, source } = sfc
-  const isProd = !!options.isProd
-  const genSourceMap = options.sourceMap !== false
-  let refBindings: string[] | undefined
-
-  const cssVars = sfc.cssVars
-  const scopeId = options.id ? options.id.replace(/^data-v-/, '') : ''
-  const scriptLang = script && script.lang
-  const scriptSetupLang = scriptSetup && scriptSetup.lang
-  const isTS =
-    scriptLang === 'ts' ||
-    scriptLang === 'tsx' ||
-    scriptSetupLang === 'ts' ||
-    scriptSetupLang === 'tsx'
-
-  // resolve parser plugins
-  const plugins: ParserPlugin[] = []
-  if (!isTS || scriptLang === 'tsx' || scriptSetupLang === 'tsx') {
-    plugins.push('jsx')
-  } else {
-    // If don't match the case of adding jsx, should remove the jsx from the babelParserPlugins
-    if (options.babelParserPlugins)
-      options.babelParserPlugins = options.babelParserPlugins.filter(
-        n => n !== 'jsx'
-      )
-  }
-  if (options.babelParserPlugins) plugins.push(...options.babelParserPlugins)
-  if (isTS) {
-    plugins.push('typescript')
-    if (!plugins.includes('decorators')) {
-      plugins.push('decorators-legacy')
-    }
-  }
-
-  if (!scriptSetup) {
-    if (!script) {
-      throw new Error(`[@vue/compiler-sfc] SFC contains no <script> tags.`)
-    }
-    if (scriptLang && !isTS && scriptLang !== 'jsx') {
-      // do not process non js/ts script blocks
-      return script
-    }
-    try {
-      let content = script.content
-      let map = script.map
-      const scriptAst = _parse(content, {
-        plugins,
-        sourceType: 'module'
-      }).program
-      const bindings = analyzeScriptBindings(scriptAst.body)
-      if (cssVars.length) {
-        content = rewriteDefault(content, DEFAULT_VAR, plugins)
-        content += genNormalScriptCssVarsCode(
-          cssVars,
-          bindings,
-          scopeId,
-          isProd
-        )
-        content += `\nexport default ${DEFAULT_VAR}`
-      }
-      return {
-        ...script,
-        content,
-        map,
-        bindings,
-        scriptAst: scriptAst.body
-      }
-    } catch (e: any) {
-      // silently fallback if parse fails since user may be using custom
-      // babel syntax
-      return script
-    }
-  }
-
-  if (script && scriptLang !== scriptSetupLang) {
-    throw new Error(
-      `[@vue/compiler-sfc] <script> and <script setup> must have the same ` +
-        `language type.`
-    )
-  }
-
-  if (scriptSetupLang && !isTS && scriptSetupLang !== 'jsx') {
-    // do not process non js/ts script blocks
-    return scriptSetup
-  }
-
-  // metadata that needs to be returned
-  const bindingMetadata: BindingMetadata = {}
-  const helperImports: Set<string> = new Set()
-  const userImports: Record<string, ImportBinding> = Object.create(null)
-  const userImportAlias: Record<string, string> = Object.create(null)
-  const scriptBindings: Record<string, BindingTypes> = Object.create(null)
-  const setupBindings: Record<string, BindingTypes> = Object.create(null)
-
-  let defaultExport: Node | undefined
-  let hasDefinePropsCall = false
-  let hasDefineEmitCall = false
-  let hasDefineExposeCall = false
-  let hasDefaultExportName = false
-  let propsRuntimeDecl: Node | undefined
-  let propsRuntimeDefaults: ObjectExpression | undefined
-  let propsDestructureDecl: Node | undefined
-  let propsDestructureRestId: string | undefined
-  let propsTypeDecl: TSTypeLiteral | TSInterfaceBody | undefined
-  let propsTypeDeclRaw: Node | undefined
-  let propsIdentifier: string | undefined
-  let emitsRuntimeDecl: Node | undefined
-  let emitsTypeDecl:
-    | TSFunctionType
-    | TSTypeLiteral
-    | TSInterfaceBody
-    | undefined
-  let emitsTypeDeclRaw: Node | undefined
-  let emitIdentifier: string | undefined
-  let hasInlinedSsrRenderFn = false
-  // props/emits declared via types
-  const typeDeclaredProps: Record<string, PropTypeData> = {}
-  const typeDeclaredEmits: Set<string> = new Set()
-  // record declared types for runtime props type generation
-  const declaredTypes: Record<string, string[]> = {}
-  // props destructure data
-  const propsDestructuredBindings: Record<
-    string, // public prop key
-    {
-      local: string // local identifier, may be different
-      default?: Expression
-    }
-  > = Object.create(null)
-
-  // magic-string state
-  const s = new MagicString(source)
-  const startOffset = scriptSetup.start
-  const endOffset = scriptSetup.end
-  const scriptStartOffset = script && script.start
-  const scriptEndOffset = script && script.end
-
-  function helper(key: string): string {
-    helperImports.add(key)
-    return `_${key}`
-  }
-
-  function parse(
-    input: string,
-    options: ParserOptions,
-    offset: number
-  ): Program {
-    try {
-      return _parse(input, options).program
-    } catch (e: any) {
-      e.message = `[@vue/compiler-sfc] ${
-        e.message
-      }\n\n${filename}\n${generateCodeFrame(
-        source,
-        e.pos + offset,
-        e.pos + offset + 1
-      )}`
-      throw e
-    }
-  }
-
-  function error(
-    msg: string,
-    node: Node,
-    end: number = node.end! + startOffset
-  ): never {
-    throw new Error(
-      `[@vue/compiler-sfc] ${msg}\n\n${filename}\n${generateCodeFrame(
-        source,
-        node.start! + startOffset,
-        end
-      )}`
-    )
-  }
-
-  function registerUserImport(
-    source: string,
-    local: string,
-    imported: string | false,
-    isType: boolean,
-    isFromSetup: boolean
-  ) {
-    if (source === 'vue' && imported) {
-      userImportAlias[imported] = local
-    }
-
-    let isUsedInTemplate = true
-    if (sfc.template && !sfc.template.src && !sfc.template.lang) {
-      isUsedInTemplate = isImportUsed(local, sfc, isTS)
-    }
-
-    userImports[local] = {
-      isType,
-      imported: imported || 'default',
-      source,
-      isFromSetup,
-      isUsedInTemplate
-    }
-  }
-
-  function processDefineProps(node: Node, declId?: LVal): boolean {
-    if (!isCallOf(node, DEFINE_PROPS)) {
-      return false
-    }
-
-    if (hasDefinePropsCall) {
-      error(`duplicate ${DEFINE_PROPS}() call`, node)
-    }
-    hasDefinePropsCall = true
-
-    propsRuntimeDecl = node.arguments[0]
-
-    // call has type parameters - infer runtime types from it
-    if (node.typeParameters) {
-      if (propsRuntimeDecl) {
-        error(
-          `${DEFINE_PROPS}() cannot accept both type and non-type arguments ` +
-            `at the same time. Use one or the other.`,
-          node
-        )
-      }
-
-      propsTypeDeclRaw = node.typeParameters.params[0]
-      propsTypeDecl = resolveQualifiedType(
-        propsTypeDeclRaw,
-        node => node.type === 'TSTypeLiteral'
-      ) as TSTypeLiteral | TSInterfaceBody | undefined
-
-      if (!propsTypeDecl) {
-        error(
-          `type argument passed to ${DEFINE_PROPS}() must be a literal type, ` +
-            `or a reference to an interface or literal type.`,
-          propsTypeDeclRaw
-        )
-      }
-    }
-
-    if (declId) {
-      propsIdentifier = scriptSetup!.content.slice(declId.start!, declId.end!)
-    }
-
-    return true
-  }
-
-  function processWithDefaults(node: Node, declId?: LVal): boolean {
-    if (!isCallOf(node, WITH_DEFAULTS)) {
-      return false
-    }
-    if (processDefineProps(node.arguments[0], declId)) {
-      if (propsRuntimeDecl) {
-        error(
-          `${WITH_DEFAULTS} can only be used with type-based ` +
-            `${DEFINE_PROPS} declaration.`,
-          node
-        )
-      }
-      if (propsDestructureDecl) {
-        error(
-          `${WITH_DEFAULTS}() is unnecessary when using destructure with ${DEFINE_PROPS}().\n` +
-            `Prefer using destructure default values, e.g. const { foo = 1 } = defineProps(...).`,
-          node.callee
-        )
-      }
-      propsRuntimeDefaults = node.arguments[1] as ObjectExpression
-      if (
-        !propsRuntimeDefaults ||
-        propsRuntimeDefaults.type !== 'ObjectExpression'
-      ) {
-        error(
-          `The 2nd argument of ${WITH_DEFAULTS} must be an object literal.`,
-          propsRuntimeDefaults || node
-        )
-      }
-    } else {
-      error(
-        `${WITH_DEFAULTS}' first argument must be a ${DEFINE_PROPS} call.`,
-        node.arguments[0] || node
-      )
-    }
-    return true
-  }
-
-  function processDefineEmits(node: Node, declId?: LVal): boolean {
-    if (!isCallOf(node, DEFINE_EMITS)) {
-      return false
-    }
-    if (hasDefineEmitCall) {
-      error(`duplicate ${DEFINE_EMITS}() call`, node)
-    }
-    hasDefineEmitCall = true
-    emitsRuntimeDecl = node.arguments[0]
-    if (node.typeParameters) {
-      if (emitsRuntimeDecl) {
-        error(
-          `${DEFINE_EMITS}() cannot accept both type and non-type arguments ` +
-            `at the same time. Use one or the other.`,
-          node
-        )
-      }
-
-      emitsTypeDeclRaw = node.typeParameters.params[0]
-      emitsTypeDecl = resolveQualifiedType(
-        emitsTypeDeclRaw,
-        node => node.type === 'TSFunctionType' || node.type === 'TSTypeLiteral'
-      ) as TSFunctionType | TSTypeLiteral | TSInterfaceBody | undefined
-
-      if (!emitsTypeDecl) {
-        error(
-          `type argument passed to ${DEFINE_EMITS}() must be a function type, ` +
-            `a literal type with call signatures, or a reference to the above types.`,
-          emitsTypeDeclRaw
-        )
-      }
-    }
-
-    if (declId) {
-      emitIdentifier =
-        declId.type === 'Identifier'
-          ? declId.name
-          : scriptSetup!.content.slice(declId.start!, declId.end!)
-    }
-
-    return true
-  }
-
-  function resolveQualifiedType(
-    node: Node,
-    qualifier: (node: Node) => boolean
-  ) {
-    if (qualifier(node)) {
-      return node
-    }
-    if (
-      node.type === 'TSTypeReference' &&
-      node.typeName.type === 'Identifier'
-    ) {
-      const refName = node.typeName.name
-      const isQualifiedType = (node: Node): Node | undefined => {
-        if (
-          node.type === 'TSInterfaceDeclaration' &&
-          node.id.name === refName
-        ) {
-          return node.body
-        } else if (
-          node.type === 'TSTypeAliasDeclaration' &&
-          node.id.name === refName &&
-          qualifier(node.typeAnnotation)
-        ) {
-          return node.typeAnnotation
-        } else if (node.type === 'ExportNamedDeclaration' && node.declaration) {
-          return isQualifiedType(node.declaration)
-        }
-      }
-      const body = scriptAst
-        ? [...scriptSetupAst.body, ...scriptAst.body]
-        : scriptSetupAst.body
-      for (const node of body) {
-        const qualified = isQualifiedType(node)
-        if (qualified) {
-          return qualified
-        }
-      }
-    }
-  }
-
-  function processDefineExpose(node: Node): boolean {
-    if (isCallOf(node, DEFINE_EXPOSE)) {
-      if (hasDefineExposeCall) {
-        error(`duplicate ${DEFINE_EXPOSE}() call`, node)
-      }
-      hasDefineExposeCall = true
-      return true
-    }
-    return false
-  }
-
-  function checkInvalidScopeReference(node: Node | undefined, method: string) {
-    if (!node) return
-    walkIdentifiers(node, id => {
-      if (setupBindings[id.name]) {
-        error(
-          `\`${method}()\` in <script setup> cannot reference locally ` +
-            `declared variables because it will be hoisted outside of the ` +
-            `setup() function. If your component options require initialization ` +
-            `in the module scope, use a separate normal <script> to export ` +
-            `the options instead.`,
-          id
-        )
-      }
-    })
-  }
-
-  /**
-   * check defaults. If the default object is an object literal with only
-   * static properties, we can directly generate more optimized default
-   * declarations. Otherwise we will have to fallback to runtime merging.
-   */
-  function hasStaticWithDefaults() {
-    return (
-      propsRuntimeDefaults &&
-      propsRuntimeDefaults.type === 'ObjectExpression' &&
-      propsRuntimeDefaults.properties.every(
-        node =>
-          (node.type === 'ObjectProperty' && !node.computed) ||
-          node.type === 'ObjectMethod'
-      )
-    )
-  }
-
-  function genRuntimeProps(props: Record<string, PropTypeData>) {
-    const keys = Object.keys(props)
-    if (!keys.length) {
-      return ``
-    }
-    const hasStaticDefaults = hasStaticWithDefaults()
-    const scriptSetupSource = scriptSetup!.content
-    let propsDecls = `{
-    ${keys
-      .map(key => {
-        let defaultString: string | undefined
-        const destructured = genDestructuredDefaultValue(key)
-        if (destructured) {
-          defaultString = `default: ${destructured}`
-        } else if (hasStaticDefaults) {
-          const prop = propsRuntimeDefaults!.properties.find(
-            (node: any) => node.key.name === key
-          ) as ObjectProperty | ObjectMethod
-          if (prop) {
-            if (prop.type === 'ObjectProperty') {
-              // prop has corresponding static default value
-              defaultString = `default: ${scriptSetupSource.slice(
-                prop.value.start!,
-                prop.value.end!
-              )}`
-            } else {
-              defaultString = `default() ${scriptSetupSource.slice(
-                prop.body.start!,
-                prop.body.end!
-              )}`
-            }
-          }
-        }
-
-        const { type, required } = props[key]
-        if (!isProd) {
-          return `${key}: { type: ${toRuntimeTypeString(
-            type
-          )}, required: ${required}${
-            defaultString ? `, ${defaultString}` : ``
-          } }`
-        } else if (
-          type.some(
-            el => el === 'Boolean' || (defaultString && el === 'Function')
-          )
-        ) {
-          // #4783 production: if boolean or defaultString and function exists, should keep the type.
-          return `${key}: { type: ${toRuntimeTypeString(type)}${
-            defaultString ? `, ${defaultString}` : ``
-          } }`
-        } else {
-          // production: checks are useless
-          return `${key}: ${defaultString ? `{ ${defaultString} }` : 'null'}`
-        }
-      })
-      .join(',\n    ')}\n  }`
-
-    if (propsRuntimeDefaults && !hasStaticDefaults) {
-      propsDecls = `${helper('mergeDefaults')}(${propsDecls}, ${source.slice(
-        propsRuntimeDefaults.start! + startOffset,
-        propsRuntimeDefaults.end! + startOffset
-      )})`
-    }
-
-    return `\n  props: ${propsDecls},`
-  }
-
-  function genDestructuredDefaultValue(key: string): string | undefined {
-    const destructured = propsDestructuredBindings[key]
-    if (destructured && destructured.default) {
-      const value = scriptSetup!.content.slice(
-        destructured.default.start!,
-        destructured.default.end!
-      )
-      const isLiteral = destructured.default.type.endsWith('Literal')
-      return isLiteral ? value : `() => (${value})`
-    }
-  }
-
-  function genSetupPropsType(node: TSTypeLiteral | TSInterfaceBody) {
-    const scriptSetupSource = scriptSetup!.content
-    if (hasStaticWithDefaults()) {
-      // if withDefaults() is used, we need to remove the optional flags
-      // on props that have default values
-      let res = `{ `
-      const members = node.type === 'TSTypeLiteral' ? node.members : node.body
-      for (const m of members) {
-        if (
-          (m.type === 'TSPropertySignature' ||
-            m.type === 'TSMethodSignature') &&
-          m.typeAnnotation &&
-          m.key.type === 'Identifier'
-        ) {
-          if (
-            propsRuntimeDefaults!.properties.some(
-              (p: any) => p.key.name === (m.key as Identifier).name
-            )
-          ) {
-            res +=
-              m.key.name +
-              (m.type === 'TSMethodSignature' ? '()' : '') +
-              scriptSetupSource.slice(
-                m.typeAnnotation.start!,
-                m.typeAnnotation.end!
-              ) +
-              ', '
-          } else {
-            res +=
-              scriptSetupSource.slice(m.start!, m.typeAnnotation.end!) + `, `
-          }
-        }
-      }
-      return (res.length ? res.slice(0, -2) : res) + ` }`
-    } else {
-      return scriptSetupSource.slice(node.start!, node.end!)
-    }
-  }
-
-  // 1. process normal <script> first if it exists
-  let scriptAst: Program | undefined
-  if (script) {
-    scriptAst = parse(
-      script.content,
-      {
-        plugins,
-        sourceType: 'module'
-      },
-      scriptStartOffset!
-    )
-
-    for (const node of scriptAst.body) {
-      if (node.type === 'ImportDeclaration') {
-        // record imports for dedupe
-        for (const specifier of node.specifiers) {
-          const imported =
-            specifier.type === 'ImportSpecifier' &&
-            specifier.imported.type === 'Identifier' &&
-            specifier.imported.name
-          registerUserImport(
-            node.source.value,
-            specifier.local.name,
-            imported,
-            node.importKind === 'type' ||
-              (specifier.type === 'ImportSpecifier' &&
-                specifier.importKind === 'type'),
-            false
-          )
-        }
-      } else if (node.type === 'ExportDefaultDeclaration') {
-        // export default
-        defaultExport = node
-
-        // check if user has manually specified `name` or 'render` option in
-        // export default
-        // if has name, skip name inference
-        // if has render and no template, generate return object instead of
-        // empty render function (#4980)
-        let optionProperties
-        if (defaultExport.declaration.type === 'ObjectExpression') {
-          optionProperties = defaultExport.declaration.properties
-        } else if (
-          defaultExport.declaration.type === 'CallExpression' &&
-          defaultExport.declaration.arguments[0].type === 'ObjectExpression'
-        ) {
-          optionProperties = defaultExport.declaration.arguments[0].properties
-        }
-        if (optionProperties) {
-          for (const s of optionProperties) {
-            if (
-              s.type === 'ObjectProperty' &&
-              s.key.type === 'Identifier' &&
-              s.key.name === 'name'
-            ) {
-              hasDefaultExportName = true
-            }
-          }
-        }
-
-        // export default { ... } --> const __default__ = { ... }
-        const start = node.start! + scriptStartOffset!
-        const end = node.declaration.start! + scriptStartOffset!
-        s.overwrite(start, end, `const ${DEFAULT_VAR} = `)
-      } else if (node.type === 'ExportNamedDeclaration') {
-        const defaultSpecifier = node.specifiers.find(
-          s => s.exported.type === 'Identifier' && s.exported.name === 'default'
-        ) as ExportSpecifier
-        if (defaultSpecifier) {
-          defaultExport = node
-          // 1. remove specifier
-          if (node.specifiers.length > 1) {
-            s.remove(
-              defaultSpecifier.start! + scriptStartOffset!,
-              defaultSpecifier.end! + scriptStartOffset!
-            )
-          } else {
-            s.remove(
-              node.start! + scriptStartOffset!,
-              node.end! + scriptStartOffset!
-            )
-          }
-          if (node.source) {
-            // export { x as default } from './x'
-            // rewrite to `import { x as __default__ } from './x'` and
-            // add to top
-            s.prepend(
-              `import { ${defaultSpecifier.local.name} as ${DEFAULT_VAR} } from '${node.source.value}'\n`
-            )
-          } else {
-            // export { x as default }
-            // rewrite to `const __default__ = x` and move to end
-            s.appendLeft(
-              scriptEndOffset!,
-              `\nconst ${DEFAULT_VAR} = ${defaultSpecifier.local.name}\n`
-            )
-          }
-        }
-        if (node.declaration) {
-          walkDeclaration(node.declaration, scriptBindings, userImportAlias)
-        }
-      } else if (
-        (node.type === 'VariableDeclaration' ||
-          node.type === 'FunctionDeclaration' ||
-          node.type === 'ClassDeclaration' ||
-          node.type === 'TSEnumDeclaration') &&
-        !node.declare
-      ) {
-        walkDeclaration(node, scriptBindings, userImportAlias)
-      }
-    }
-
-    // apply reactivity transform
-    // if (enableReactivityTransform && shouldTransform(script.content)) {
-    //   const { rootRefs, importedHelpers } = transformAST(
-    //     scriptAst,
-    //     s,
-    //     scriptStartOffset!
-    //   )
-    //   refBindings = rootRefs
-    //   for (const h of importedHelpers) {
-    //     helperImports.add(h)
-    //   }
-    // }
-
-    // <script> after <script setup>
-    // we need to move the block up so that `const __default__` is
-    // declared before being used in the actual component definition
-    if (scriptStartOffset! > startOffset) {
-      // if content doesn't end with newline, add one
-      if (!/\n$/.test(script.content.trim())) {
-        s.appendLeft(scriptEndOffset!, `\n`)
-      }
-      s.move(scriptStartOffset!, scriptEndOffset!, 0)
-    }
-  }
-
-  // 2. parse <script setup> and  walk over top level statements
-  const scriptSetupAst = parse(
-    scriptSetup.content,
-    {
-      plugins: [
-        ...plugins,
-        // allow top level await but only inside <script setup>
-        'topLevelAwait'
-      ],
-      sourceType: 'module'
-    },
-    startOffset
-  )
-
-  for (const node of scriptSetupAst.body) {
-    const start = node.start! + startOffset
-    let end = node.end! + startOffset
-    // locate comment
-    if (node.trailingComments && node.trailingComments.length > 0) {
-      const lastCommentNode =
-        node.trailingComments[node.trailingComments.length - 1]
-      end = lastCommentNode.end! + startOffset
-    }
-    // locate the end of whitespace between this statement and the next
-    while (end <= source.length) {
-      if (!/\s/.test(source.charAt(end))) {
-        break
-      }
-      end++
-    }
-
-    // (Dropped) `ref: x` bindings
-    if (
-      node.type === 'LabeledStatement' &&
-      node.label.name === 'ref' &&
-      node.body.type === 'ExpressionStatement'
-    ) {
-      error(
-        `ref sugar using the label syntax was an experimental proposal and ` +
-          `has been dropped based on community feedback. Please check out ` +
-          `the new proposal at https://github.com/vuejs/rfcs/discussions/369`,
-        node
-      )
-    }
-
-    if (node.type === 'ImportDeclaration') {
-      // import declarations are moved to top
-      s.move(start, end, 0)
-
-      // dedupe imports
-      let removed = 0
-      const removeSpecifier = (i: number) => {
-        const removeLeft = i > removed
-        removed++
-        const current = node.specifiers[i]
-        const next = node.specifiers[i + 1]
-        s.remove(
-          removeLeft
-            ? node.specifiers[i - 1].end! + startOffset
-            : current.start! + startOffset,
-          next && !removeLeft
-            ? next.start! + startOffset
-            : current.end! + startOffset
-        )
-      }
-
-      for (let i = 0; i < node.specifiers.length; i++) {
-        const specifier = node.specifiers[i]
-        const local = specifier.local.name
-        let imported =
-          specifier.type === 'ImportSpecifier' &&
-          specifier.imported.type === 'Identifier' &&
-          specifier.imported.name
-        if (specifier.type === 'ImportNamespaceSpecifier') {
-          imported = '*'
-        }
-        const source = node.source.value
-        const existing = userImports[local]
-        if (
-          source === 'vue' &&
-          (imported === DEFINE_PROPS ||
-            imported === DEFINE_EMITS ||
-            imported === DEFINE_EXPOSE)
-        ) {
-          warnOnce(
-            `\`${imported}\` is a compiler macro and no longer needs to be imported.`
-          )
-          removeSpecifier(i)
-        } else if (existing) {
-          if (existing.source === source && existing.imported === imported) {
-            // already imported in <script setup>, dedupe
-            removeSpecifier(i)
-          } else {
-            error(`different imports aliased to same local name.`, specifier)
-          }
-        } else {
-          registerUserImport(
-            source,
-            local,
-            imported,
-            node.importKind === 'type' ||
-              (specifier.type === 'ImportSpecifier' &&
-                specifier.importKind === 'type'),
-            true
-          )
-        }
-      }
-      if (node.specifiers.length && removed === node.specifiers.length) {
-        s.remove(node.start! + startOffset, node.end! + startOffset)
-      }
-    }
-
-    if (node.type === 'ExpressionStatement') {
-      // process `defineProps` and `defineEmit(s)` calls
-      if (
-        processDefineProps(node.expression) ||
-        processDefineEmits(node.expression) ||
-        processWithDefaults(node.expression)
-      ) {
-        s.remove(node.start! + startOffset, node.end! + startOffset)
-      } else if (processDefineExpose(node.expression)) {
-        // defineExpose({}) -> expose({})
-        const callee = (node.expression as CallExpression).callee
-        s.overwrite(
-          callee.start! + startOffset,
-          callee.end! + startOffset,
-          'expose'
-        )
-      }
-    }
-
-    if (node.type === 'VariableDeclaration' && !node.declare) {
-      const total = node.declarations.length
-      let left = total
-      for (let i = 0; i < total; i++) {
-        const decl = node.declarations[i]
-        if (decl.init) {
-          // defineProps / defineEmits
-          const isDefineProps =
-            processDefineProps(decl.init, decl.id) ||
-            processWithDefaults(decl.init, decl.id)
-          const isDefineEmits = processDefineEmits(decl.init, decl.id)
-          if (isDefineProps || isDefineEmits) {
-            if (left === 1) {
-              s.remove(node.start! + startOffset, node.end! + startOffset)
-            } else {
-              let start = decl.start! + startOffset
-              let end = decl.end! + startOffset
-              if (i === 0) {
-                // first one, locate the start of the next
-                end = node.declarations[i + 1].start! + startOffset
-              } else {
-                // not first one, locate the end of the prev
-                start = node.declarations[i - 1].end! + startOffset
-              }
-              s.remove(start, end)
-              left--
-            }
-          }
-        }
-      }
-    }
-
-    // walk declarations to record declared bindings
-    if (
-      (node.type === 'VariableDeclaration' ||
-        node.type === 'FunctionDeclaration' ||
-        node.type === 'ClassDeclaration') &&
-      !node.declare
-    ) {
-      walkDeclaration(node, setupBindings, userImportAlias)
-    }
-
-    // walk statements & named exports / variable declarations for top level
-    // await
-    if (
-      (node.type === 'VariableDeclaration' && !node.declare) ||
-      node.type.endsWith('Statement')
-    ) {
-      const scope: Statement[][] = [scriptSetupAst.body]
-      ;(walk as any)(node, {
-        enter(child: Node, parent: Node) {
-          if (isFunctionType(child)) {
-            this.skip()
-          }
-          if (child.type === 'BlockStatement') {
-            scope.push(child.body)
-          }
-          if (child.type === 'AwaitExpression') {
-            error(
-              `Vue 2 does not support top level await in <script setup>.`,
-              child
-            )
-          }
-        },
-        exit(node: Node) {
-          if (node.type === 'BlockStatement') scope.pop()
-        }
-      })
-    }
-
-    if (
-      (node.type === 'ExportNamedDeclaration' && node.exportKind !== 'type') ||
-      node.type === 'ExportAllDeclaration' ||
-      node.type === 'ExportDefaultDeclaration'
-    ) {
-      error(
-        `<script setup> cannot contain ES module exports. ` +
-          `If you are using a previous version of <script setup>, please ` +
-          `consult the updated RFC at https://github.com/vuejs/rfcs/pull/227.`,
-        node
-      )
-    }
-
-    if (isTS) {
-      // runtime enum
-      if (node.type === 'TSEnumDeclaration') {
-        registerBinding(setupBindings, node.id, BindingTypes.SETUP_CONST)
-      }
-
-      // move all Type declarations to outer scope
-      if (
-        node.type.startsWith('TS') ||
-        (node.type === 'ExportNamedDeclaration' &&
-          node.exportKind === 'type') ||
-        (node.type === 'VariableDeclaration' && node.declare)
-      ) {
-        recordType(node, declaredTypes)
-        s.move(start, end, 0)
-      }
-    }
-  }
-
-  // 3. Apply reactivity transform
-  // if (
-  //   (enableReactivityTransform &&
-  //     // normal <script> had ref bindings that maybe used in <script setup>
-  //     (refBindings || shouldTransform(scriptSetup.content))) ||
-  //   propsDestructureDecl
-  // ) {
-  //   const { rootRefs, importedHelpers } = transformAST(
-  //     scriptSetupAst,
-  //     s,
-  //     startOffset,
-  //     refBindings,
-  //     propsDestructuredBindings
-  //   )
-  //   refBindings = refBindings ? [...refBindings, ...rootRefs] : rootRefs
-  //   for (const h of importedHelpers) {
-  //     helperImports.add(h)
-  //   }
-  // }
-
-  // 4. extract runtime props/emits code from setup context type
-  if (propsTypeDecl) {
-    extractRuntimeProps(propsTypeDecl, typeDeclaredProps, declaredTypes, isProd)
-  }
-  if (emitsTypeDecl) {
-    extractRuntimeEmits(emitsTypeDecl, typeDeclaredEmits)
-  }
-
-  // 5. check useOptions args to make sure it doesn't reference setup scope
-  // variables
-  checkInvalidScopeReference(propsRuntimeDecl, DEFINE_PROPS)
-  checkInvalidScopeReference(propsRuntimeDefaults, DEFINE_PROPS)
-  checkInvalidScopeReference(propsDestructureDecl, DEFINE_PROPS)
-  checkInvalidScopeReference(emitsRuntimeDecl, DEFINE_EMITS)
-
-  // 6. remove non-script content
-  if (script) {
-    if (startOffset < scriptStartOffset!) {
-      // <script setup> before <script>
-      s.remove(0, startOffset)
-      s.remove(endOffset, scriptStartOffset!)
-      s.remove(scriptEndOffset!, source.length)
-    } else {
-      // <script> before <script setup>
-      s.remove(0, scriptStartOffset!)
-      s.remove(scriptEndOffset!, startOffset)
-      s.remove(endOffset, source.length)
-    }
-  } else {
-    // only <script setup>
-    s.remove(0, startOffset)
-    s.remove(endOffset, source.length)
-  }
-
-  // 7. analyze binding metadata
-  if (scriptAst) {
-    Object.assign(bindingMetadata, analyzeScriptBindings(scriptAst.body))
-  }
-  if (propsRuntimeDecl) {
-    for (const key of getObjectOrArrayExpressionKeys(propsRuntimeDecl)) {
-      bindingMetadata[key] = BindingTypes.PROPS
-    }
-  }
-  for (const key in typeDeclaredProps) {
-    bindingMetadata[key] = BindingTypes.PROPS
-  }
-  // props aliases
-  // if (propsDestructureDecl) {
-  //   if (propsDestructureRestId) {
-  //     bindingMetadata[propsDestructureRestId] =
-  //       BindingTypes.SETUP_REACTIVE_CONST
-  //   }
-  //   for (const key in propsDestructuredBindings) {
-  //     const { local } = propsDestructuredBindings[key]
-  //     if (local !== key) {
-  //       bindingMetadata[local] = BindingTypes.PROPS_ALIASED
-  //       ;(bindingMetadata.__propsAliases ||
-  //         (bindingMetadata.__propsAliases = {}))[local] = key
-  //     }
-  //   }
-  // }
-  for (const [key, { isType, imported, source }] of Object.entries(
-    userImports
-  )) {
-    if (isType) continue
-    bindingMetadata[key] =
-      imported === '*' ||
-      (imported === 'default' && source.endsWith('.vue')) ||
-      source === 'vue'
-        ? BindingTypes.SETUP_CONST
-        : BindingTypes.SETUP_MAYBE_REF
-  }
-  for (const key in scriptBindings) {
-    bindingMetadata[key] = scriptBindings[key]
-  }
-  for (const key in setupBindings) {
-    bindingMetadata[key] = setupBindings[key]
-  }
-  // known ref bindings
-  if (refBindings) {
-    for (const key of refBindings) {
-      bindingMetadata[key] = BindingTypes.SETUP_REF
-    }
-  }
-
-  // 8. inject `useCssVars` calls
-  if (cssVars.length) {
-    helperImports.add(CSS_VARS_HELPER)
-    s.prependRight(
-      startOffset,
-      `\n${genCssVarsCode(cssVars, bindingMetadata, scopeId, isProd)}\n`
-    )
-  }
-
-  // 9. finalize setup() argument signature
-  let args = `__props`
-  if (propsTypeDecl) {
-    // mark as any and only cast on assignment
-    // since the user defined complex types may be incompatible with the
-    // inferred type from generated runtime declarations
-    args += `: any`
-  }
-  // inject user assignment of props
-  // we use a default __props so that template expressions referencing props
-  // can use it directly
-  if (propsIdentifier) {
-    s.prependLeft(
-      startOffset,
-      `\nconst ${propsIdentifier} = __props${
-        propsTypeDecl ? ` as ${genSetupPropsType(propsTypeDecl)}` : ``
-      };\n`
-    )
-  }
-  if (propsDestructureRestId) {
-    s.prependLeft(
-      startOffset,
-      `\nconst ${propsDestructureRestId} = ${helper(
-        `createPropsRestProxy`
-      )}(__props, ${JSON.stringify(Object.keys(propsDestructuredBindings))});\n`
-    )
-  }
-
-  const destructureElements = hasDefineExposeCall ? [`expose`] : []
-  if (emitIdentifier) {
-    destructureElements.push(
-      emitIdentifier === `emit` ? `emit` : `emit: ${emitIdentifier}`
-    )
-  }
-  if (destructureElements.length) {
-    args += `, { ${destructureElements.join(', ')} }`
-    if (emitsTypeDecl) {
-      args += `: { emit: (${scriptSetup.content.slice(
-        emitsTypeDecl.start!,
-        emitsTypeDecl.end!
-      )}), expose: any, slots: any, attrs: any }`
-    }
-  }
-
-  // 10. generate return statement
-  const allBindings: Record<string, any> = {
-    ...scriptBindings,
-    ...setupBindings
-  }
-  for (const key in userImports) {
-    if (!userImports[key].isType && userImports[key].isUsedInTemplate) {
-      allBindings[key] = true
-    }
-  }
-  // __sfc marker indicates these bindings are compiled from <script setup>
-  // and should not be proxied on `this`
-  const returned = `{ ${__TEST__ ? `` : `__sfc: true,`}${Object.keys(
-    allBindings
-  ).join(', ')} }`
-
-  s.appendRight(endOffset, `\nreturn ${returned}\n}\n\n`)
-
-  // 11. finalize default export
-  let runtimeOptions = ``
-  if (!hasDefaultExportName && filename && filename !== DEFAULT_FILENAME) {
-    const match = filename.match(/([^/\\]+)\.\w+$/)
-    if (match) {
-      runtimeOptions += `\n  __name: '${match[1]}',`
-    }
-  }
-  if (hasInlinedSsrRenderFn) {
-    runtimeOptions += `\n  __ssrInlineRender: true,`
-  }
-  if (propsRuntimeDecl) {
-    let declCode = scriptSetup.content
-      .slice(propsRuntimeDecl.start!, propsRuntimeDecl.end!)
-      .trim()
-    if (propsDestructureDecl) {
-      const defaults: string[] = []
-      for (const key in propsDestructuredBindings) {
-        const d = genDestructuredDefaultValue(key)
-        if (d) defaults.push(`${key}: ${d}`)
-      }
-      if (defaults.length) {
-        declCode = `${helper(
-          `mergeDefaults`
-        )}(${declCode}, {\n  ${defaults.join(',\n  ')}\n})`
-      }
-    }
-    runtimeOptions += `\n  props: ${declCode},`
-  } else if (propsTypeDecl) {
-    runtimeOptions += genRuntimeProps(typeDeclaredProps)
-  }
-  if (emitsRuntimeDecl) {
-    runtimeOptions += `\n  emits: ${scriptSetup.content
-      .slice(emitsRuntimeDecl.start!, emitsRuntimeDecl.end!)
-      .trim()},`
-  } else if (emitsTypeDecl) {
-    runtimeOptions += genRuntimeEmits(typeDeclaredEmits)
-  }
-
-  // wrap setup code with function.
-  if (isTS) {
-    // for TS, make sure the exported type is still valid type with
-    // correct props information
-    // we have to use object spread for types to be merged properly
-    // user's TS setting should compile it down to proper targets
-    // export default defineComponent({ ...__default__, ... })
-    const def = defaultExport ? `\n  ...${DEFAULT_VAR},` : ``
-    s.prependLeft(
-      startOffset,
-      `\nexport default /*#__PURE__*/${helper(
-        `defineComponent`
-      )}({${def}${runtimeOptions}\n  setup(${args}) {\n`
-    )
-    s.appendRight(endOffset, `})`)
-  } else {
-    if (defaultExport) {
-      // without TS, can't rely on rest spread, so we use Object.assign
-      // export default Object.assign(__default__, { ... })
-      s.prependLeft(
-        startOffset,
-        `\nexport default /*#__PURE__*/Object.assign(${DEFAULT_VAR}, {${runtimeOptions}\n  ` +
-          `setup(${args}) {\n`
-      )
-      s.appendRight(endOffset, `})`)
-    } else {
-      s.prependLeft(
-        startOffset,
-        `\nexport default {${runtimeOptions}\n  setup(${args}) {\n`
-      )
-      s.appendRight(endOffset, `}`)
-    }
-  }
-
-  // 12. finalize Vue helper imports
-  if (helperImports.size > 0) {
-    s.prepend(
-      `import { ${[...helperImports]
-        .map(h => `${h} as _${h}`)
-        .join(', ')} } from 'vue'\n`
-    )
-  }
-
-  s.trim()
-
-  return {
-    ...scriptSetup,
-    bindings: bindingMetadata,
-    imports: userImports,
-    content: s.toString(),
-    map: genSourceMap
-      ? (s.generateMap({
-          source: filename,
-          hires: true,
-          includeContent: true
-        }) as unknown as RawSourceMap)
-      : undefined,
-    scriptAst: scriptAst?.body,
-    scriptSetupAst: scriptSetupAst?.body
-  }
-}
-
-function registerBinding(
-  bindings: Record<string, BindingTypes>,
-  node: Identifier,
-  type: BindingTypes
-) {
-  bindings[node.name] = type
-}
-
-function walkDeclaration(
-  node: Declaration,
-  bindings: Record<string, BindingTypes>,
-  userImportAlias: Record<string, string>
-) {
-  if (node.type === 'VariableDeclaration') {
-    const isConst = node.kind === 'const'
-    // export const foo = ...
-    for (const { id, init } of node.declarations) {
-      const isDefineCall = !!(
-        isConst &&
-        isCallOf(
-          init,
-          c => c === DEFINE_PROPS || c === DEFINE_EMITS || c === WITH_DEFAULTS
-        )
-      )
-      if (id.type === 'Identifier') {
-        let bindingType
-        const userReactiveBinding = userImportAlias['reactive'] || 'reactive'
-        if (isCallOf(init, userReactiveBinding)) {
-          // treat reactive() calls as let since it's meant to be mutable
-          bindingType = isConst
-            ? BindingTypes.SETUP_REACTIVE_CONST
-            : BindingTypes.SETUP_LET
-        } else if (
-          // if a declaration is a const literal, we can mark it so that
-          // the generated render fn code doesn't need to unref() it
-          isDefineCall ||
-          (isConst && canNeverBeRef(init!, userReactiveBinding))
-        ) {
-          bindingType = isCallOf(init, DEFINE_PROPS)
-            ? BindingTypes.SETUP_REACTIVE_CONST
-            : BindingTypes.SETUP_CONST
-        } else if (isConst) {
-          if (isCallOf(init, userImportAlias['ref'] || 'ref')) {
-            bindingType = BindingTypes.SETUP_REF
-          } else {
-            bindingType = BindingTypes.SETUP_MAYBE_REF
-          }
-        } else {
-          bindingType = BindingTypes.SETUP_LET
-        }
-        registerBinding(bindings, id, bindingType)
-      } else {
-        if (isCallOf(init, DEFINE_PROPS)) {
-          // skip walking props destructure
-          return
-        }
-        if (id.type === 'ObjectPattern') {
-          walkObjectPattern(id, bindings, isConst, isDefineCall)
-        } else if (id.type === 'ArrayPattern') {
-          walkArrayPattern(id, bindings, isConst, isDefineCall)
-        }
-      }
-    }
-  } else if (
-    node.type === 'TSEnumDeclaration' ||
-    node.type === 'FunctionDeclaration' ||
-    node.type === 'ClassDeclaration'
-  ) {
-    // export function foo() {} / export class Foo {}
-    // export declarations must be named.
-    bindings[node.id!.name] = BindingTypes.SETUP_CONST
-  }
-}
-
-function walkObjectPattern(
-  node: ObjectPattern,
-  bindings: Record<string, BindingTypes>,
-  isConst: boolean,
-  isDefineCall = false
-) {
-  for (const p of node.properties) {
-    if (p.type === 'ObjectProperty') {
-      if (p.key.type === 'Identifier' && p.key === p.value) {
-        // shorthand: const { x } = ...
-        const type = isDefineCall
-          ? BindingTypes.SETUP_CONST
-          : isConst
-          ? BindingTypes.SETUP_MAYBE_REF
-          : BindingTypes.SETUP_LET
-        registerBinding(bindings, p.key, type)
-      } else {
-        walkPattern(p.value, bindings, isConst, isDefineCall)
-      }
-    } else {
-      // ...rest
-      // argument can only be identifier when destructuring
-      const type = isConst ? BindingTypes.SETUP_CONST : BindingTypes.SETUP_LET
-      registerBinding(bindings, p.argument as Identifier, type)
-    }
-  }
-}
-
-function walkArrayPattern(
-  node: ArrayPattern,
-  bindings: Record<string, BindingTypes>,
-  isConst: boolean,
-  isDefineCall = false
-) {
-  for (const e of node.elements) {
-    e && walkPattern(e, bindings, isConst, isDefineCall)
-  }
-}
-
-function walkPattern(
-  node: Node,
-  bindings: Record<string, BindingTypes>,
-  isConst: boolean,
-  isDefineCall = false
-) {
-  if (node.type === 'Identifier') {
-    const type = isDefineCall
-      ? BindingTypes.SETUP_CONST
-      : isConst
-      ? BindingTypes.SETUP_MAYBE_REF
-      : BindingTypes.SETUP_LET
-    registerBinding(bindings, node, type)
-  } else if (node.type === 'RestElement') {
-    // argument can only be identifier when destructuring
-    const type = isConst ? BindingTypes.SETUP_CONST : BindingTypes.SETUP_LET
-    registerBinding(bindings, node.argument as Identifier, type)
-  } else if (node.type === 'ObjectPattern') {
-    walkObjectPattern(node, bindings, isConst)
-  } else if (node.type === 'ArrayPattern') {
-    walkArrayPattern(node, bindings, isConst)
-  } else if (node.type === 'AssignmentPattern') {
-    if (node.left.type === 'Identifier') {
-      const type = isDefineCall
-        ? BindingTypes.SETUP_CONST
-        : isConst
-        ? BindingTypes.SETUP_MAYBE_REF
-        : BindingTypes.SETUP_LET
-      registerBinding(bindings, node.left, type)
-    } else {
-      walkPattern(node.left, bindings, isConst)
-    }
-  }
-}
-
-interface PropTypeData {
-  key: string
-  type: string[]
-  required: boolean
-}
-
-function recordType(node: Node, declaredTypes: Record<string, string[]>) {
-  if (node.type === 'TSInterfaceDeclaration') {
-    declaredTypes[node.id.name] = [`Object`]
-  } else if (node.type === 'TSTypeAliasDeclaration') {
-    declaredTypes[node.id.name] = inferRuntimeType(
-      node.typeAnnotation,
-      declaredTypes
-    )
-  } else if (node.type === 'ExportNamedDeclaration' && node.declaration) {
-    recordType(node.declaration, declaredTypes)
-  }
-}
-
-function extractRuntimeProps(
-  node: TSTypeLiteral | TSInterfaceBody,
-  props: Record<string, PropTypeData>,
-  declaredTypes: Record<string, string[]>,
-  isProd: boolean
-) {
-  const members = node.type === 'TSTypeLiteral' ? node.members : node.body
-  for (const m of members) {
-    if (
-      (m.type === 'TSPropertySignature' || m.type === 'TSMethodSignature') &&
-      m.key.type === 'Identifier'
-    ) {
-      let type
-      if (m.type === 'TSMethodSignature') {
-        type = ['Function']
-      } else if (m.typeAnnotation) {
-        type = inferRuntimeType(m.typeAnnotation.typeAnnotation, declaredTypes)
-      }
-      props[m.key.name] = {
-        key: m.key.name,
-        required: !m.optional,
-        type: type || [`null`]
-      }
-    }
-  }
-}
-
-function inferRuntimeType(
-  node: TSType,
-  declaredTypes: Record<string, string[]>
-): string[] {
-  switch (node.type) {
-    case 'TSStringKeyword':
-      return ['String']
-    case 'TSNumberKeyword':
-      return ['Number']
-    case 'TSBooleanKeyword':
-      return ['Boolean']
-    case 'TSObjectKeyword':
-      return ['Object']
-    case 'TSTypeLiteral':
-      // TODO (nice to have) generate runtime property validation
-      return ['Object']
-    case 'TSFunctionType':
-      return ['Function']
-    case 'TSArrayType':
-    case 'TSTupleType':
-      // TODO (nice to have) generate runtime element type/length checks
-      return ['Array']
-
-    case 'TSLiteralType':
-      switch (node.literal.type) {
-        case 'StringLiteral':
-          return ['String']
-        case 'BooleanLiteral':
-          return ['Boolean']
-        case 'NumericLiteral':
-        case 'BigIntLiteral':
-          return ['Number']
-        default:
-          return [`null`]
-      }
-
-    case 'TSTypeReference':
-      if (node.typeName.type === 'Identifier') {
-        if (declaredTypes[node.typeName.name]) {
-          return declaredTypes[node.typeName.name]
-        }
-        switch (node.typeName.name) {
-          case 'Array':
-          case 'Function':
-          case 'Object':
-          case 'Set':
-          case 'Map':
-          case 'WeakSet':
-          case 'WeakMap':
-          case 'Date':
-          case 'Promise':
-            return [node.typeName.name]
-          case 'Record':
-          case 'Partial':
-          case 'Readonly':
-          case 'Pick':
-          case 'Omit':
-          case 'Exclude':
-          case 'Extract':
-          case 'Required':
-          case 'InstanceType':
-            return ['Object']
-        }
-      }
-      return [`null`]
-
-    case 'TSParenthesizedType':
-      return inferRuntimeType(node.typeAnnotation, declaredTypes)
-    case 'TSUnionType':
-      return [
-        ...new Set(
-          [].concat(
-            ...(node.types.map(t => inferRuntimeType(t, declaredTypes)) as any)
-          )
-        )
-      ]
-    case 'TSIntersectionType':
-      return ['Object']
-
-    case 'TSSymbolKeyword':
-      return ['Symbol']
-
-    default:
-      return [`null`] // no runtime check
-  }
-}
-
-function toRuntimeTypeString(types: string[]) {
-  return types.length > 1 ? `[${types.join(', ')}]` : types[0]
-}
-
-function extractRuntimeEmits(
-  node: TSFunctionType | TSTypeLiteral | TSInterfaceBody,
-  emits: Set<string>
-) {
-  if (node.type === 'TSTypeLiteral' || node.type === 'TSInterfaceBody') {
-    const members = node.type === 'TSTypeLiteral' ? node.members : node.body
-    for (let t of members) {
-      if (t.type === 'TSCallSignatureDeclaration') {
-        extractEventNames(t.parameters[0], emits)
-      }
-    }
-    return
-  } else {
-    extractEventNames(node.parameters[0], emits)
-  }
-}
-
-function extractEventNames(
-  eventName: ArrayPattern | Identifier | ObjectPattern | RestElement,
-  emits: Set<string>
-) {
-  if (
-    eventName.type === 'Identifier' &&
-    eventName.typeAnnotation &&
-    eventName.typeAnnotation.type === 'TSTypeAnnotation'
-  ) {
-    const typeNode = eventName.typeAnnotation.typeAnnotation
-    if (typeNode.type === 'TSLiteralType') {
-      if (
-        typeNode.literal.type !== 'UnaryExpression' &&
-        typeNode.literal.type !== 'TemplateLiteral'
-      ) {
-        emits.add(String(typeNode.literal.value))
-      }
-    } else if (typeNode.type === 'TSUnionType') {
-      for (const t of typeNode.types) {
-        if (
-          t.type === 'TSLiteralType' &&
-          t.literal.type !== 'UnaryExpression' &&
-          t.literal.type !== 'TemplateLiteral'
-        ) {
-          emits.add(String(t.literal.value))
-        }
-      }
-    }
-  }
-}
-
-function genRuntimeEmits(emits: Set<string>) {
-  return emits.size
-    ? `\n  emits: [${Array.from(emits)
-        .map(p => JSON.stringify(p))
-        .join(', ')}],`
-    : ``
-}
-
-function isCallOf(
-  node: Node | null | undefined,
-  test: string | ((id: string) => boolean)
-): node is CallExpression {
-  return !!(
-    node &&
-    node.type === 'CallExpression' &&
-    node.callee.type === 'Identifier' &&
-    (typeof test === 'string'
-      ? node.callee.name === test
-      : test(node.callee.name))
-  )
-}
-
-function canNeverBeRef(node: Node, userReactiveImport: string): boolean {
-  if (isCallOf(node, userReactiveImport)) {
-    return true
-  }
-  switch (node.type) {
-    case 'UnaryExpression':
-    case 'BinaryExpression':
-    case 'ArrayExpression':
-    case 'ObjectExpression':
-    case 'FunctionExpression':
-    case 'ArrowFunctionExpression':
-    case 'UpdateExpression':
-    case 'ClassExpression':
-    case 'TaggedTemplateExpression':
-      return true
-    case 'SequenceExpression':
-      return canNeverBeRef(
-        node.expressions[node.expressions.length - 1],
-        userReactiveImport
-      )
-    default:
-      if (node.type.endsWith('Literal')) {
-        return true
-      }
-      return false
-  }
-}
-
-/**
- * Analyze bindings in normal `<script>`
- * Note that `compileScriptSetup` already analyzes bindings as part of its
- * compilation process so this should only be used on single `<script>` SFCs.
- */
-function analyzeScriptBindings(ast: Statement[]): BindingMetadata {
-  for (const node of ast) {
-    if (
-      node.type === 'ExportDefaultDeclaration' &&
-      node.declaration.type === 'ObjectExpression'
-    ) {
-      return analyzeBindingsFromOptions(node.declaration)
-    }
-  }
-  return {}
-}
-
-function analyzeBindingsFromOptions(node: ObjectExpression): BindingMetadata {
-  const bindings: BindingMetadata = {}
-  // #3270, #3275
-  // mark non-script-setup so we don't resolve components/directives from these
-  Object.defineProperty(bindings, '__isScriptSetup', {
-    enumerable: false,
-    value: false
-  })
-  for (const property of node.properties) {
-    if (
-      property.type === 'ObjectProperty' &&
-      !property.computed &&
-      property.key.type === 'Identifier'
-    ) {
-      // props
-      if (property.key.name === 'props') {
-        // props: ['foo']
-        // props: { foo: ... }
-        for (const key of getObjectOrArrayExpressionKeys(property.value)) {
-          bindings[key] = BindingTypes.PROPS
-        }
-      }
-
-      // inject
-      else if (property.key.name === 'inject') {
-        // inject: ['foo']
-        // inject: { foo: {} }
-        for (const key of getObjectOrArrayExpressionKeys(property.value)) {
-          bindings[key] = BindingTypes.OPTIONS
-        }
-      }
-
-      // computed & methods
-      else if (
-        property.value.type === 'ObjectExpression' &&
-        (property.key.name === 'computed' || property.key.name === 'methods')
-      ) {
-        // methods: { foo() {} }
-        // computed: { foo() {} }
-        for (const key of getObjectExpressionKeys(property.value)) {
-          bindings[key] = BindingTypes.OPTIONS
-        }
-      }
-    }
-
-    // setup & data
-    else if (
-      property.type === 'ObjectMethod' &&
-      property.key.type === 'Identifier' &&
-      (property.key.name === 'setup' || property.key.name === 'data')
-    ) {
-      for (const bodyItem of property.body.body) {
-        // setup() {
-        //   return {
-        //     foo: null
-        //   }
-        // }
-        if (
-          bodyItem.type === 'ReturnStatement' &&
-          bodyItem.argument &&
-          bodyItem.argument.type === 'ObjectExpression'
-        ) {
-          for (const key of getObjectExpressionKeys(bodyItem.argument)) {
-            bindings[key] =
-              property.key.name === 'setup'
-                ? BindingTypes.SETUP_MAYBE_REF
-                : BindingTypes.DATA
-          }
-        }
-      }
-    }
-  }
-
-  return bindings
-}
-
-function getObjectExpressionKeys(node: ObjectExpression): string[] {
-  const keys: string[] = []
-  for (const prop of node.properties) {
-    if (
-      (prop.type === 'ObjectProperty' || prop.type === 'ObjectMethod') &&
-      !prop.computed
-    ) {
-      if (prop.key.type === 'Identifier') {
-        keys.push(prop.key.name)
-      } else if (prop.key.type === 'StringLiteral') {
-        keys.push(prop.key.value)
-      }
-    }
-  }
-  return keys
-}
-
-function getArrayExpressionKeys(node: ArrayExpression): string[] {
-  const keys: string[] = []
-  for (const element of node.elements) {
-    if (element && element.type === 'StringLiteral') {
-      keys.push(element.value)
-    }
-  }
-  return keys
-}
-
-function getObjectOrArrayExpressionKeys(value: Node): string[] {
-  if (value.type === 'ArrayExpression') {
-    return getArrayExpressionKeys(value)
-  }
-  if (value.type === 'ObjectExpression') {
-    return getObjectExpressionKeys(value)
-  }
-  return []
-}
-
-const templateUsageCheckCache = new LRU<string, string>(512)
-
-function resolveTemplateUsageCheckString(sfc: SFCDescriptor, isTS: boolean) {
-  const { content } = sfc.template!
-  const cached = templateUsageCheckCache.get(content)
-  if (cached) {
-    return cached
-  }
-
-  let code = ''
-
-  parseHTML(content, {
-    ...webCompilerOptions,
-    start(tag, attrs) {
-      if (!isBuiltInTag(tag) && !isReservedTag(tag)) {
-        code += `,${camelize(tag)},${capitalize(camelize(tag))}`
-      }
-      for (let i = 0; i < attrs.length; i++) {
-        const { name, value } = attrs[i]
-        if (dirRE.test(name)) {
-          const baseName = onRE.test(name)
-            ? 'on'
-            : slotRE.test(name)
-            ? 'slot'
-            : bindRE.test(name)
-            ? 'bind'
-            : name.replace(dirRE, '')
-          if (!isBuiltInDir(baseName)) {
-            code += `,v${capitalize(camelize(baseName))}`
-          }
-          if (value) {
-            code += `,${processExp(value, isTS, baseName)}`
-          }
-        } else if (name === 'ref') {
-          code += `,${value}`
-        }
-      }
-    },
-    chars(text) {
-      const res = parseText(text)
-      if (res) {
-        code += `,${processExp(res.expression, isTS)}`
-      }
-    }
-  })
-
-  code += ';'
-  templateUsageCheckCache.set(content, code)
-  return code
-}
-
-const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
-
-function processExp(exp: string, isTS: boolean, dir?: string): string {
-  if (isTS && / as\s+\w|<.*>|:/.test(exp)) {
-    if (dir === 'slot') {
-      exp = `(${exp})=>{}`
-    } else if (dir === 'on') {
-      exp = `()=>{return ${exp}}`
-    } else if (dir === 'for') {
-      const inMatch = exp.match(forAliasRE)
-      if (inMatch) {
-        const [, LHS, RHS] = inMatch
-        return processExp(`(${LHS})=>{}`, true) + processExp(RHS, true)
-      }
-    }
-    let ret = ''
-    // has potential type cast or generic arguments that uses types
-    const ast = parseExpression(exp, { plugins: ['typescript'] })
-    walkIdentifiers(ast, node => {
-      ret += `,` + node.name
-    })
-    return ret
-  }
-  return stripStrings(exp)
-}
-
-function stripStrings(exp: string) {
-  return exp
-    .replace(/'[^']*'|"[^"]*"/g, '')
-    .replace(/`[^`]+`/g, stripTemplateString)
-}
-
-function stripTemplateString(str: string): string {
-  const interpMatch = str.match(/\${[^}]+}/g)
-  if (interpMatch) {
-    return interpMatch.map(m => m.slice(2, -1)).join(',')
-  }
-  return ''
-}
-
-function isImportUsed(
-  local: string,
-  sfc: SFCDescriptor,
-  isTS: boolean
-): boolean {
-  return new RegExp(
-    // #4274 escape $ since it's a special char in regex
-    // (and is the only regex special char that is valid in identifiers)
-    `[^\\w$_]${local.replace(/\$/g, '\\$')}[^\\w$_]`
-  ).test(resolveTemplateUsageCheckString(sfc, isTS))
-}
-
-/**
- * Note: this comparison assumes the prev/next script are already identical,
- * and only checks the special case where <script setup> unused import
- * pruning result changes due to template changes.
- */
-export function hmrShouldReload(
-  prevImports: Record<string, ImportBinding>,
-  next: SFCDescriptor
-): boolean {
-  if (!next.scriptSetup) {
-    return false
-  }
-
-  const isTS = next.scriptSetup.lang === 'ts' || next.scriptSetup.lang === 'tsx'
-  // for each previous import, check if its used status remain the same based on
-  // the next descriptor's template
-  for (const key in prevImports) {
-    // if an import was previous unused, but now is used, we need to force
-    // reload so that the script now includes that import.
-    if (!prevImports[key].isUsedInTemplate && isImportUsed(key, next, isTS)) {
-      return true
-    }
-  }
-
-  return false
-}
diff --git a/packages/compiler-sfc/src/compileStyle.ts b/packages/compiler-sfc/src/compileStyle.ts
deleted file mode 100644
index 92698982664..00000000000
--- a/packages/compiler-sfc/src/compileStyle.ts
+++ /dev/null
@@ -1,147 +0,0 @@
-const postcss = require('postcss')
-import { ProcessOptions, LazyResult } from 'postcss'
-import trimPlugin from './stylePlugins/trim'
-import scopedPlugin from './stylePlugins/scoped'
-import {
-  processors,
-  StylePreprocessor,
-  StylePreprocessorResults
-} from './stylePreprocessors'
-import { cssVarsPlugin } from './cssVars'
-
-export interface SFCStyleCompileOptions {
-  source: string
-  filename: string
-  id: string
-  map?: any
-  scoped?: boolean
-  trim?: boolean
-  preprocessLang?: string
-  preprocessOptions?: any
-  postcssOptions?: any
-  postcssPlugins?: any[]
-  isProd?: boolean
-}
-
-export interface SFCAsyncStyleCompileOptions extends SFCStyleCompileOptions {
-  isAsync?: boolean
-}
-
-export interface SFCStyleCompileResults {
-  code: string
-  map: any | void
-  rawResult: LazyResult | void
-  errors: string[]
-}
-
-export function compileStyle(
-  options: SFCStyleCompileOptions
-): SFCStyleCompileResults {
-  return doCompileStyle({ ...options, isAsync: false })
-}
-
-export function compileStyleAsync(
-  options: SFCStyleCompileOptions
-): Promise<SFCStyleCompileResults> {
-  return Promise.resolve(doCompileStyle({ ...options, isAsync: true }))
-}
-
-export function doCompileStyle(
-  options: SFCAsyncStyleCompileOptions
-): SFCStyleCompileResults {
-  const {
-    filename,
-    id,
-    scoped = true,
-    trim = true,
-    isProd = false,
-    preprocessLang,
-    postcssOptions,
-    postcssPlugins
-  } = options
-  const preprocessor = preprocessLang && processors[preprocessLang]
-  const preProcessedSource = preprocessor && preprocess(options, preprocessor)
-  const map = preProcessedSource ? preProcessedSource.map : options.map
-  const source = preProcessedSource ? preProcessedSource.code : options.source
-
-  const plugins = (postcssPlugins || []).slice()
-  plugins.unshift(cssVarsPlugin({ id: id.replace(/^data-v-/, ''), isProd }))
-  if (trim) {
-    plugins.push(trimPlugin())
-  }
-  if (scoped) {
-    plugins.push(scopedPlugin(id))
-  }
-
-  const postCSSOptions: ProcessOptions = {
-    ...postcssOptions,
-    to: filename,
-    from: filename
-  }
-  if (map) {
-    postCSSOptions.map = {
-      inline: false,
-      annotation: false,
-      prev: map
-    }
-  }
-
-  let result, code, outMap
-  const errors: any[] = []
-  if (preProcessedSource && preProcessedSource.errors.length) {
-    errors.push(...preProcessedSource.errors)
-  }
-  try {
-    result = postcss(plugins).process(source, postCSSOptions)
-
-    // In async mode, return a promise.
-    if (options.isAsync) {
-      return result
-        .then(
-          (result: LazyResult): SFCStyleCompileResults => ({
-            code: result.css || '',
-            map: result.map && result.map.toJSON(),
-            errors,
-            rawResult: result
-          })
-        )
-        .catch(
-          (error: Error): SFCStyleCompileResults => ({
-            code: '',
-            map: undefined,
-            errors: [...errors, error.message],
-            rawResult: undefined
-          })
-        )
-    }
-
-    // force synchronous transform (we know we only have sync plugins)
-    code = result.css
-    outMap = result.map
-  } catch (e) {
-    errors.push(e)
-  }
-
-  return {
-    code: code || ``,
-    map: outMap && outMap.toJSON(),
-    errors,
-    rawResult: result
-  }
-}
-
-function preprocess(
-  options: SFCStyleCompileOptions,
-  preprocessor: StylePreprocessor
-): StylePreprocessorResults {
-  return preprocessor(
-    options.source,
-    options.map,
-    Object.assign(
-      {
-        filename: options.filename
-      },
-      options.preprocessOptions
-    )
-  )
-}
diff --git a/packages/compiler-sfc/src/compileTemplate.ts b/packages/compiler-sfc/src/compileTemplate.ts
deleted file mode 100644
index ebc6f6c6f2a..00000000000
--- a/packages/compiler-sfc/src/compileTemplate.ts
+++ /dev/null
@@ -1,205 +0,0 @@
-import { BindingMetadata, TemplateCompiler } from './types'
-import assetUrlsModule, {
-  AssetURLOptions,
-  TransformAssetUrlsOptions
-} from './templateCompilerModules/assetUrl'
-import srcsetModule from './templateCompilerModules/srcset'
-import consolidate from '@vue/consolidate'
-import * as _compiler from 'web/entry-compiler'
-import { prefixIdentifiers } from './prefixIdentifiers'
-import { CompilerOptions, WarningMessage } from 'types/compiler'
-
-export interface SFCTemplateCompileOptions {
-  source: string
-  filename: string
-  compiler?: TemplateCompiler
-  compilerOptions?: CompilerOptions
-  transformAssetUrls?: AssetURLOptions | boolean
-  transformAssetUrlsOptions?: TransformAssetUrlsOptions
-  preprocessLang?: string
-  preprocessOptions?: any
-  transpileOptions?: any
-  isProduction?: boolean
-  isFunctional?: boolean
-  optimizeSSR?: boolean
-  prettify?: boolean
-  isTS?: boolean
-  bindings?: BindingMetadata
-}
-
-export interface SFCTemplateCompileResults {
-  ast: Object | undefined
-  code: string
-  source: string
-  tips: (string | WarningMessage)[]
-  errors: (string | WarningMessage)[]
-}
-
-export function compileTemplate(
-  options: SFCTemplateCompileOptions
-): SFCTemplateCompileResults {
-  const { preprocessLang } = options
-  const preprocessor = preprocessLang && consolidate[preprocessLang]
-  if (preprocessor) {
-    return actuallyCompile(
-      Object.assign({}, options, {
-        source: preprocess(options, preprocessor)
-      })
-    )
-  } else if (preprocessLang) {
-    return {
-      ast: {},
-      code: `var render = function () {}\n` + `var staticRenderFns = []\n`,
-      source: options.source,
-      tips: [
-        `Component ${options.filename} uses lang ${preprocessLang} for template. Please install the language preprocessor.`
-      ],
-      errors: [
-        `Component ${options.filename} uses lang ${preprocessLang} for template, however it is not installed.`
-      ]
-    }
-  } else {
-    return actuallyCompile(options)
-  }
-}
-
-function preprocess(
-  options: SFCTemplateCompileOptions,
-  preprocessor: any
-): string {
-  const { source, filename, preprocessOptions } = options
-
-  const finalPreprocessOptions = Object.assign(
-    {
-      filename
-    },
-    preprocessOptions
-  )
-
-  // Consolidate exposes a callback based API, but the callback is in fact
-  // called synchronously for most templating engines. In our case, we have to
-  // expose a synchronous API so that it is usable in Jest transforms (which
-  // have to be sync because they are applied via Node.js require hooks)
-  let res: any, err
-  preprocessor.render(
-    source,
-    finalPreprocessOptions,
-    (_err: Error | null, _res: string) => {
-      if (_err) err = _err
-      res = _res
-    }
-  )
-
-  if (err) throw err
-  return res
-}
-
-function actuallyCompile(
-  options: SFCTemplateCompileOptions
-): SFCTemplateCompileResults {
-  const {
-    source,
-    compiler = _compiler,
-    compilerOptions = {},
-    transpileOptions = {},
-    transformAssetUrls,
-    transformAssetUrlsOptions,
-    isProduction = process.env.NODE_ENV === 'production',
-    isFunctional = false,
-    optimizeSSR = false,
-    prettify = true,
-    isTS = false,
-    bindings
-  } = options
-
-  const compile =
-    optimizeSSR && compiler.ssrCompile ? compiler.ssrCompile : compiler.compile
-
-  let finalCompilerOptions = compilerOptions
-  if (transformAssetUrls) {
-    const builtInModules = [
-      transformAssetUrls === true
-        ? assetUrlsModule(undefined, transformAssetUrlsOptions)
-        : assetUrlsModule(transformAssetUrls, transformAssetUrlsOptions),
-      srcsetModule(transformAssetUrlsOptions)
-    ]
-    finalCompilerOptions = Object.assign({}, compilerOptions, {
-      modules: [...builtInModules, ...(compilerOptions.modules || [])],
-      filename: options.filename
-    })
-  }
-  finalCompilerOptions.bindings = bindings
-
-  const { ast, render, staticRenderFns, tips, errors } = compile(
-    source,
-    finalCompilerOptions
-  )
-
-  if (errors && errors.length) {
-    return {
-      ast,
-      code: `var render = function () {}\n` + `var staticRenderFns = []\n`,
-      source,
-      tips,
-      errors
-    }
-  } else {
-    // stripping `with` usage
-    let code =
-      `var __render__ = ${prefixIdentifiers(
-        `function render(${isFunctional ? `_c,_vm` : ``}){${render}\n}`,
-        isFunctional,
-        isTS,
-        transpileOptions,
-        bindings
-      )}\n` +
-      `var __staticRenderFns__ = [${staticRenderFns.map(code =>
-        prefixIdentifiers(
-          `function (${isFunctional ? `_c,_vm` : ``}){${code}\n}`,
-          isFunctional,
-          isTS,
-          transpileOptions,
-          bindings
-        )
-      )}]` +
-      `\n`
-
-    // #23 we use __render__ to avoid `render` not being prefixed by the
-    // transpiler when stripping with, but revert it back to `render` to
-    // maintain backwards compat
-    code = code.replace(/\s__(render|staticRenderFns)__\s/g, ' $1 ')
-
-    if (!isProduction) {
-      // mark with stripped (this enables Vue to use correct runtime proxy
-      // detection)
-      code += `render._withStripped = true`
-
-      if (prettify) {
-        try {
-          code = require('prettier').format(code, {
-            semi: false,
-            parser: 'babel'
-          })
-        } catch (e: any) {
-          if (e.code === 'MODULE_NOT_FOUND') {
-            tips.push(
-              'The `prettify` option is on, but the dependency `prettier` is not found.\n' +
-                'Please either turn off `prettify` or manually install `prettier`.'
-            )
-          }
-          tips.push(
-            `Failed to prettify component ${options.filename} template source after compilation.`
-          )
-        }
-      }
-    }
-
-    return {
-      ast,
-      code,
-      source,
-      tips,
-      errors
-    }
-  }
-}
diff --git a/packages/compiler-sfc/src/cssVars.ts b/packages/compiler-sfc/src/cssVars.ts
deleted file mode 100644
index 48f8cb70244..00000000000
--- a/packages/compiler-sfc/src/cssVars.ts
+++ /dev/null
@@ -1,179 +0,0 @@
-import { BindingMetadata } from './types'
-import { SFCDescriptor } from './parseComponent'
-import { PluginCreator } from 'postcss'
-import hash from 'hash-sum'
-import { prefixIdentifiers } from './prefixIdentifiers'
-
-export const CSS_VARS_HELPER = `useCssVars`
-
-export function genCssVarsFromList(
-  vars: string[],
-  id: string,
-  isProd: boolean,
-  isSSR = false
-): string {
-  return `{\n  ${vars
-    .map(
-      key => `"${isSSR ? `--` : ``}${genVarName(id, key, isProd)}": (${key})`
-    )
-    .join(',\n  ')}\n}`
-}
-
-function genVarName(id: string, raw: string, isProd: boolean): string {
-  if (isProd) {
-    return hash(id + raw)
-  } else {
-    return `${id}-${raw.replace(/([^\w-])/g, '_')}`
-  }
-}
-
-function normalizeExpression(exp: string) {
-  exp = exp.trim()
-  if (
-    (exp[0] === `'` && exp[exp.length - 1] === `'`) ||
-    (exp[0] === `"` && exp[exp.length - 1] === `"`)
-  ) {
-    return exp.slice(1, -1)
-  }
-  return exp
-}
-
-const vBindRE = /v-bind\s*\(/g
-
-export function parseCssVars(sfc: SFCDescriptor): string[] {
-  const vars: string[] = []
-  sfc.styles.forEach(style => {
-    let match
-    // ignore v-bind() in comments /* ... */
-    const content = style.content.replace(/\/\*([\s\S]*?)\*\//g, '')
-    while ((match = vBindRE.exec(content))) {
-      const start = match.index + match[0].length
-      const end = lexBinding(content, start)
-      if (end !== null) {
-        const variable = normalizeExpression(content.slice(start, end))
-        if (!vars.includes(variable)) {
-          vars.push(variable)
-        }
-      }
-    }
-  })
-  return vars
-}
-
-const enum LexerState {
-  inParens,
-  inSingleQuoteString,
-  inDoubleQuoteString
-}
-
-function lexBinding(content: string, start: number): number | null {
-  let state: LexerState = LexerState.inParens
-  let parenDepth = 0
-
-  for (let i = start; i < content.length; i++) {
-    const char = content.charAt(i)
-    switch (state) {
-      case LexerState.inParens:
-        if (char === `'`) {
-          state = LexerState.inSingleQuoteString
-        } else if (char === `"`) {
-          state = LexerState.inDoubleQuoteString
-        } else if (char === `(`) {
-          parenDepth++
-        } else if (char === `)`) {
-          if (parenDepth > 0) {
-            parenDepth--
-          } else {
-            return i
-          }
-        }
-        break
-      case LexerState.inSingleQuoteString:
-        if (char === `'`) {
-          state = LexerState.inParens
-        }
-        break
-      case LexerState.inDoubleQuoteString:
-        if (char === `"`) {
-          state = LexerState.inParens
-        }
-        break
-    }
-  }
-  return null
-}
-
-// for compileStyle
-export interface CssVarsPluginOptions {
-  id: string
-  isProd: boolean
-}
-
-export const cssVarsPlugin: PluginCreator<CssVarsPluginOptions> = opts => {
-  const { id, isProd } = opts!
-  return {
-    postcssPlugin: 'vue-sfc-vars',
-    Declaration(decl) {
-      // rewrite CSS variables
-      const value = decl.value
-      if (vBindRE.test(value)) {
-        vBindRE.lastIndex = 0
-        let transformed = ''
-        let lastIndex = 0
-        let match
-        while ((match = vBindRE.exec(value))) {
-          const start = match.index + match[0].length
-          const end = lexBinding(value, start)
-          if (end !== null) {
-            const variable = normalizeExpression(value.slice(start, end))
-            transformed +=
-              value.slice(lastIndex, match.index) +
-              `var(--${genVarName(id, variable, isProd)})`
-            lastIndex = end + 1
-          }
-        }
-        decl.value = transformed + value.slice(lastIndex)
-      }
-    }
-  }
-}
-cssVarsPlugin.postcss = true
-
-export function genCssVarsCode(
-  vars: string[],
-  bindings: BindingMetadata,
-  id: string,
-  isProd: boolean
-) {
-  const varsExp = genCssVarsFromList(vars, id, isProd)
-  return `_${CSS_VARS_HELPER}((_vm, _setup) => ${prefixIdentifiers(
-    `(${varsExp})`,
-    false,
-    false,
-    undefined,
-    bindings
-  )})`
-}
-
-// <script setup> already gets the calls injected as part of the transform
-// this is only for single normal <script>
-export function genNormalScriptCssVarsCode(
-  cssVars: string[],
-  bindings: BindingMetadata,
-  id: string,
-  isProd: boolean
-): string {
-  return (
-    `\nimport { ${CSS_VARS_HELPER} as _${CSS_VARS_HELPER} } from 'vue'\n` +
-    `const __injectCSSVars__ = () => {\n${genCssVarsCode(
-      cssVars,
-      bindings,
-      id,
-      isProd
-    )}}\n` +
-    `const __setup__ = __default__.setup\n` +
-    `__default__.setup = __setup__\n` +
-    `  ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }\n` +
-    `  : __injectCSSVars__\n`
-  )
-}
diff --git a/packages/compiler-sfc/src/index.ts b/packages/compiler-sfc/src/index.ts
deleted file mode 100644
index fc050c52e57..00000000000
--- a/packages/compiler-sfc/src/index.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-// API
-export { parse } from './parse'
-export { compileTemplate } from './compileTemplate'
-export { compileStyle, compileStyleAsync } from './compileStyle'
-export { compileScript } from './compileScript'
-export { generateCodeFrame } from 'compiler/codeframe'
-export { rewriteDefault } from './rewriteDefault'
-
-// For backwards compat only. Some existing tools like
-// fork-ts-checker-webpack-plugin relies on its presence for differentiating
-// between Vue 2 and Vue 3.
-// ref #12719
-// ref https://github.com/TypeStrong/fork-ts-checker-webpack-plugin/issues/765
-export { parseComponent } from './parseComponent'
-
-// types
-export { SFCParseOptions } from './parse'
-export { CompilerOptions, WarningMessage } from 'types/compiler'
-export { TemplateCompiler } from './types'
-export {
-  SFCBlock,
-  SFCCustomBlock,
-  SFCScriptBlock,
-  SFCDescriptor
-} from './parseComponent'
-export {
-  SFCTemplateCompileOptions,
-  SFCTemplateCompileResults
-} from './compileTemplate'
-export { SFCStyleCompileOptions, SFCStyleCompileResults } from './compileStyle'
-export { SFCScriptCompileOptions } from './compileScript'
diff --git a/packages/compiler-sfc/src/parse.ts b/packages/compiler-sfc/src/parse.ts
deleted file mode 100644
index 4b73a7ab01f..00000000000
--- a/packages/compiler-sfc/src/parse.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-import { SourceMapGenerator } from 'source-map'
-import { RawSourceMap, TemplateCompiler } from './types'
-import {
-  parseComponent,
-  VueTemplateCompilerParseOptions,
-  SFCDescriptor,
-  DEFAULT_FILENAME
-} from './parseComponent'
-
-import hash from 'hash-sum'
-import LRU from 'lru-cache'
-import { hmrShouldReload } from './compileScript'
-import { parseCssVars } from './cssVars'
-
-const cache = new LRU<string, SFCDescriptor>(100)
-
-const splitRE = /\r?\n/g
-const emptyRE = /^(?:\/\/)?\s*$/
-
-export interface SFCParseOptions {
-  source: string
-  filename?: string
-  compiler?: TemplateCompiler
-  compilerParseOptions?: VueTemplateCompilerParseOptions
-  sourceRoot?: string
-  sourceMap?: boolean
-  /**
-   * @deprecated use `sourceMap` instead.
-   */
-  needMap?: boolean
-}
-
-export function parse(options: SFCParseOptions): SFCDescriptor {
-  const {
-    source,
-    filename = DEFAULT_FILENAME,
-    compiler,
-    compilerParseOptions = { pad: false } as VueTemplateCompilerParseOptions,
-    sourceRoot = '',
-    needMap = true,
-    sourceMap = needMap
-  } = options
-  const cacheKey = hash(
-    filename + source + JSON.stringify(compilerParseOptions)
-  )
-
-  let output = cache.get(cacheKey)
-  if (output) {
-    return output
-  }
-
-  if (compiler) {
-    // user-provided compiler
-    output = compiler.parseComponent(source, compilerParseOptions)
-  } else {
-    // use built-in compiler
-    output = parseComponent(source, compilerParseOptions)
-  }
-
-  output.filename = filename
-
-  // parse CSS vars
-  output.cssVars = parseCssVars(output)
-
-  output.shouldForceReload = prevImports =>
-    hmrShouldReload(prevImports, output!)
-
-  if (sourceMap) {
-    if (output.script && !output.script.src) {
-      output.script.map = generateSourceMap(
-        filename,
-        source,
-        output.script.content,
-        sourceRoot,
-        compilerParseOptions.pad
-      )
-    }
-    if (output.styles) {
-      output.styles.forEach(style => {
-        if (!style.src) {
-          style.map = generateSourceMap(
-            filename,
-            source,
-            style.content,
-            sourceRoot,
-            compilerParseOptions.pad
-          )
-        }
-      })
-    }
-  }
-
-  cache.set(cacheKey, output)
-  return output
-}
-
-function generateSourceMap(
-  filename: string,
-  source: string,
-  generated: string,
-  sourceRoot: string,
-  pad?: 'line' | 'space' | boolean
-): RawSourceMap {
-  const map = new SourceMapGenerator({
-    file: filename.replace(/\\/g, '/'),
-    sourceRoot: sourceRoot.replace(/\\/g, '/')
-  })
-  let offset = 0
-  if (!pad) {
-    offset = source.split(generated).shift()!.split(splitRE).length - 1
-  }
-  map.setSourceContent(filename, source)
-  generated.split(splitRE).forEach((line, index) => {
-    if (!emptyRE.test(line)) {
-      map.addMapping({
-        source: filename,
-        original: {
-          line: index + 1 + offset,
-          column: 0
-        },
-        generated: {
-          line: index + 1,
-          column: 0
-        }
-      })
-    }
-  })
-  return JSON.parse(map.toString())
-}
diff --git a/packages/compiler-sfc/src/parseComponent.ts b/packages/compiler-sfc/src/parseComponent.ts
deleted file mode 100644
index 05489280e63..00000000000
--- a/packages/compiler-sfc/src/parseComponent.ts
+++ /dev/null
@@ -1,220 +0,0 @@
-import deindent from 'de-indent'
-import { parseHTML } from 'compiler/parser/html-parser'
-import { makeMap } from 'shared/util'
-import { ASTAttr, WarningMessage } from 'types/compiler'
-import { BindingMetadata, RawSourceMap } from './types'
-import type { ImportBinding } from './compileScript'
-
-export const DEFAULT_FILENAME = 'anonymous.vue'
-
-const splitRE = /\r?\n/g
-const replaceRE = /./g
-const isSpecialTag = makeMap('script,style,template', true)
-
-export interface SFCCustomBlock {
-  type: string
-  content: string
-  attrs: { [key: string]: string | true }
-  start: number
-  end: number
-  src?: string
-  map?: RawSourceMap
-}
-
-export interface SFCBlock extends SFCCustomBlock {
-  lang?: string
-  scoped?: boolean
-  module?: string | boolean
-}
-
-export interface SFCScriptBlock extends SFCBlock {
-  type: 'script'
-  setup?: string | boolean
-  bindings?: BindingMetadata
-  imports?: Record<string, ImportBinding>
-  /**
-   * import('\@babel/types').Statement
-   */
-  scriptAst?: any[]
-  /**
-   * import('\@babel/types').Statement
-   */
-  scriptSetupAst?: any[]
-}
-
-export interface SFCDescriptor {
-  source: string
-  filename: string
-  template: SFCBlock | null
-  script: SFCScriptBlock | null
-  scriptSetup: SFCScriptBlock | null
-  styles: SFCBlock[]
-  customBlocks: SFCCustomBlock[]
-  cssVars: string[]
-
-  errors: (string | WarningMessage)[]
-
-  /**
-   * compare with an existing descriptor to determine whether HMR should perform
-   * a reload vs. re-render.
-   *
-   * Note: this comparison assumes the prev/next script are already identical,
-   * and only checks the special case where `<script setup lang="ts">` unused
-   * import pruning result changes due to template changes.
-   */
-  shouldForceReload: (prevImports: Record<string, ImportBinding>) => boolean
-}
-
-export interface VueTemplateCompilerParseOptions {
-  pad?: 'line' | 'space' | boolean
-  deindent?: boolean
-  outputSourceRange?: boolean
-}
-
-/**
- * Parse a single-file component (*.vue) file into an SFC Descriptor Object.
- */
-export function parseComponent(
-  source: string,
-  options: VueTemplateCompilerParseOptions = {}
-): SFCDescriptor {
-  const sfc: SFCDescriptor = {
-    source,
-    filename: DEFAULT_FILENAME,
-    template: null,
-    script: null,
-    scriptSetup: null, // TODO
-    styles: [],
-    customBlocks: [],
-    cssVars: [],
-    errors: [],
-    shouldForceReload: null as any // attached in parse() by compiler-sfc
-  }
-  let depth = 0
-  let currentBlock: SFCBlock | null = null
-
-  let warn: any = msg => {
-    sfc.errors.push(msg)
-  }
-
-  if (__DEV__ && options.outputSourceRange) {
-    warn = (msg, range) => {
-      const data: WarningMessage = { msg }
-      if (range.start != null) {
-        data.start = range.start
-      }
-      if (range.end != null) {
-        data.end = range.end
-      }
-      sfc.errors.push(data)
-    }
-  }
-
-  function start(
-    tag: string,
-    attrs: ASTAttr[],
-    unary: boolean,
-    start: number,
-    end: number
-  ) {
-    if (depth === 0) {
-      currentBlock = {
-        type: tag,
-        content: '',
-        start: end,
-        end: 0, // will be set on tag close
-        attrs: attrs.reduce((cumulated, { name, value }) => {
-          cumulated[name] = value || true
-          return cumulated
-        }, {})
-      }
-
-      if (typeof currentBlock.attrs.src === 'string') {
-        currentBlock.src = currentBlock.attrs.src
-      }
-
-      if (isSpecialTag(tag)) {
-        checkAttrs(currentBlock, attrs)
-        if (tag === 'script') {
-          const block = currentBlock as SFCScriptBlock
-          if (block.attrs.setup) {
-            block.setup = currentBlock.attrs.setup
-            sfc.scriptSetup = block
-          } else {
-            sfc.script = block
-          }
-        } else if (tag === 'style') {
-          sfc.styles.push(currentBlock)
-        } else {
-          sfc[tag] = currentBlock
-        }
-      } else {
-        // custom blocks
-        sfc.customBlocks.push(currentBlock)
-      }
-    }
-    if (!unary) {
-      depth++
-    }
-  }
-
-  function checkAttrs(block: SFCBlock, attrs: ASTAttr[]) {
-    for (let i = 0; i < attrs.length; i++) {
-      const attr = attrs[i]
-      if (attr.name === 'lang') {
-        block.lang = attr.value
-      }
-      if (attr.name === 'scoped') {
-        block.scoped = true
-      }
-      if (attr.name === 'module') {
-        block.module = attr.value || true
-      }
-    }
-  }
-
-  function end(tag: string, start: number) {
-    if (depth === 1 && currentBlock) {
-      currentBlock.end = start
-      let text = source.slice(currentBlock.start, currentBlock.end)
-      if (
-        options.deindent === true ||
-        // by default, deindent unless it's script with default lang or (j/t)sx?
-        (options.deindent !== false &&
-          !(
-            currentBlock.type === 'script' &&
-            (!currentBlock.lang || /^(j|t)sx?$/.test(currentBlock.lang))
-          ))
-      ) {
-        text = deindent(text)
-      }
-      // pad content so that linters and pre-processors can output correct
-      // line numbers in errors and warnings
-      if (currentBlock.type !== 'template' && options.pad) {
-        text = padContent(currentBlock, options.pad) + text
-      }
-      currentBlock.content = text
-      currentBlock = null
-    }
-    depth--
-  }
-
-  function padContent(block: SFCBlock, pad: true | 'line' | 'space') {
-    if (pad === 'space') {
-      return source.slice(0, block.start).replace(replaceRE, ' ')
-    } else {
-      const offset = source.slice(0, block.start).split(splitRE).length
-      const padChar = block.type === 'script' && !block.lang ? '//\n' : '\n'
-      return Array(offset).join(padChar)
-    }
-  }
-
-  parseHTML(source, {
-    warn,
-    start,
-    end,
-    outputSourceRange: options.outputSourceRange
-  })
-
-  return sfc
-}
diff --git a/packages/compiler-sfc/src/prefixIdentifiers.ts b/packages/compiler-sfc/src/prefixIdentifiers.ts
deleted file mode 100644
index 25ea5b9109b..00000000000
--- a/packages/compiler-sfc/src/prefixIdentifiers.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import MagicString from 'magic-string'
-import { parseExpression, ParserOptions, ParserPlugin } from '@babel/parser'
-import { makeMap } from 'shared/util'
-import { isStaticProperty, walkIdentifiers } from './babelUtils'
-import { BindingMetadata } from './types'
-
-const doNotPrefix = makeMap(
-  'Infinity,undefined,NaN,isFinite,isNaN,' +
-    'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
-    'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +
-    'require,' + // for webpack
-    'arguments,' + // parsed as identifier but is a special keyword...
-    '_c' // cached to save property access
-)
-
-/**
- * The input is expected to be a valid expression.
- */
-export function prefixIdentifiers(
-  source: string,
-  isFunctional = false,
-  isTS = false,
-  babelOptions: ParserOptions = {},
-  bindings?: BindingMetadata
-) {
-  const s = new MagicString(source)
-
-  const plugins: ParserPlugin[] = [
-    ...(isTS ? (['typescript'] as const) : []),
-    ...(babelOptions?.plugins || [])
-  ]
-
-  const ast = parseExpression(source, {
-    ...babelOptions,
-    plugins
-  })
-
-  const isScriptSetup = bindings && bindings.__isScriptSetup !== false
-
-  walkIdentifiers(
-    ast,
-    (ident, parent) => {
-      const { name } = ident
-      if (doNotPrefix(name)) {
-        return
-      }
-
-      let prefix = `_vm.`
-      if (isScriptSetup) {
-        const type = bindings[name]
-        if (type && type.startsWith('setup')) {
-          prefix = `_setup.`
-        }
-      }
-
-      if (isStaticProperty(parent) && parent.shorthand) {
-        // property shorthand like { foo }, we need to add the key since
-        // we rewrite the value
-        // { foo } -> { foo: _vm.foo }
-        s.appendLeft(ident.end!, `: ${prefix}${name}`)
-      } else {
-        s.prependRight(ident.start!, prefix)
-      }
-    },
-    node => {
-      if (node.type === 'WithStatement') {
-        s.remove(node.start!, node.body.start! + 1)
-        s.remove(node.end! - 1, node.end!)
-        if (!isFunctional) {
-          s.prependRight(
-            node.start!,
-            `var _vm=this,_c=_vm._self._c${
-              isScriptSetup ? `,_setup=_vm._self._setupProxy;` : `;`
-            }`
-          )
-        }
-      }
-    }
-  )
-
-  return s.toString()
-}
diff --git a/packages/compiler-sfc/src/rewriteDefault.ts b/packages/compiler-sfc/src/rewriteDefault.ts
deleted file mode 100644
index 8161da01118..00000000000
--- a/packages/compiler-sfc/src/rewriteDefault.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import { parse, ParserPlugin } from '@babel/parser'
-import MagicString from 'magic-string'
-
-const defaultExportRE = /((?:^|\n|;)\s*)export(\s*)default/
-const namedDefaultExportRE = /((?:^|\n|;)\s*)export(.+)(?:as)?(\s*)default/s
-const exportDefaultClassRE =
-  /((?:^|\n|;)\s*)export\s+default\s+class\s+([\w$]+)/
-
-/**
- * Utility for rewriting `export default` in a script block into a variable
- * declaration so that we can inject things into it
- */
-export function rewriteDefault(
-  input: string,
-  as: string,
-  parserPlugins?: ParserPlugin[]
-): string {
-  if (!hasDefaultExport(input)) {
-    return input + `\nconst ${as} = {}`
-  }
-
-  let replaced: string | undefined
-
-  const classMatch = input.match(exportDefaultClassRE)
-  if (classMatch) {
-    replaced =
-      input.replace(exportDefaultClassRE, '$1class $2') +
-      `\nconst ${as} = ${classMatch[2]}`
-  } else {
-    replaced = input.replace(defaultExportRE, `$1const ${as} =`)
-  }
-  if (!hasDefaultExport(replaced)) {
-    return replaced
-  }
-
-  // if the script somehow still contains `default export`, it probably has
-  // multi-line comments or template strings. fallback to a full parse.
-  const s = new MagicString(input)
-  const ast = parse(input, {
-    sourceType: 'module',
-    plugins: parserPlugins
-  }).program.body
-  ast.forEach(node => {
-    if (node.type === 'ExportDefaultDeclaration') {
-      if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
-        let start: number =
-          node.declaration.decorators && node.declaration.decorators.length > 0
-            ? node.declaration.decorators[
-                node.declaration.decorators.length - 1
-              ].end!
-            : node.start!
-        s.overwrite(start, node.declaration.id.start!, ` class `)
-        s.append(`\nconst ${as} = ${node.declaration.id.name}`)
-      } else {
-        s.overwrite(node.start!, node.declaration.start!, `const ${as} = `)
-      }
-    }
-    if (node.type === 'ExportNamedDeclaration') {
-      for (const specifier of node.specifiers) {
-        if (
-          specifier.type === 'ExportSpecifier' &&
-          specifier.exported.type === 'Identifier' &&
-          specifier.exported.name === 'default'
-        ) {
-          if (node.source) {
-            if (specifier.local.name === 'default') {
-              const end = specifierEnd(input, specifier.local.end!, node.end)
-              s.prepend(
-                `import { default as __VUE_DEFAULT__ } from '${node.source.value}'\n`
-              )
-              s.overwrite(specifier.start!, end, ``)
-              s.append(`\nconst ${as} = __VUE_DEFAULT__`)
-              continue
-            } else {
-              const end = specifierEnd(input, specifier.exported.end!, node.end)
-              s.prepend(
-                `import { ${input.slice(
-                  specifier.local.start!,
-                  specifier.local.end!
-                )} } from '${node.source.value}'\n`
-              )
-              s.overwrite(specifier.start!, end, ``)
-              s.append(`\nconst ${as} = ${specifier.local.name}`)
-              continue
-            }
-          }
-          const end = specifierEnd(input, specifier.end!, node.end)
-          s.overwrite(specifier.start!, end, ``)
-          s.append(`\nconst ${as} = ${specifier.local.name}`)
-        }
-      }
-    }
-  })
-  return s.toString()
-}
-
-export function hasDefaultExport(input: string): boolean {
-  return defaultExportRE.test(input) || namedDefaultExportRE.test(input)
-}
-
-function specifierEnd(
-  input: string,
-  end: number,
-  nodeEnd: number | undefined | null
-) {
-  // export { default   , foo } ...
-  let hasCommas = false
-  let oldEnd = end
-  while (end < nodeEnd!) {
-    if (/\s/.test(input.charAt(end))) {
-      end++
-    } else if (input.charAt(end) === ',') {
-      end++
-      hasCommas = true
-      break
-    } else if (input.charAt(end) === '}') {
-      break
-    }
-  }
-  return hasCommas ? end : oldEnd
-}
diff --git a/packages/compiler-sfc/src/stylePlugins/scoped.ts b/packages/compiler-sfc/src/stylePlugins/scoped.ts
deleted file mode 100644
index 55f17c386f3..00000000000
--- a/packages/compiler-sfc/src/stylePlugins/scoped.ts
+++ /dev/null
@@ -1,203 +0,0 @@
-import { PluginCreator, Rule, AtRule } from 'postcss'
-import selectorParser from 'postcss-selector-parser'
-
-const animationNameRE = /^(-\w+-)?animation-name$/
-const animationRE = /^(-\w+-)?animation$/
-
-const scopedPlugin: PluginCreator<string> = (id = '') => {
-  const keyframes = Object.create(null)
-  const shortId = id.replace(/^data-v-/, '')
-
-  return {
-    postcssPlugin: 'vue-sfc-scoped',
-    Rule(rule) {
-      processRule(id, rule)
-    },
-    AtRule(node) {
-      if (
-        /-?keyframes$/.test(node.name) &&
-        !node.params.endsWith(`-${shortId}`)
-      ) {
-        // register keyframes
-        keyframes[node.params] = node.params = node.params + '-' + shortId
-      }
-    },
-    OnceExit(root) {
-      if (Object.keys(keyframes).length) {
-        // If keyframes are found in this <style>, find and rewrite animation names
-        // in declarations.
-        // Caveat: this only works for keyframes and animation rules in the same
-        // <style> element.
-        // individual animation-name declaration
-        root.walkDecls(decl => {
-          if (animationNameRE.test(decl.prop)) {
-            decl.value = decl.value
-              .split(',')
-              .map(v => keyframes[v.trim()] || v.trim())
-              .join(',')
-          }
-          // shorthand
-          if (animationRE.test(decl.prop)) {
-            decl.value = decl.value
-              .split(',')
-              .map(v => {
-                const vals = v.trim().split(/\s+/)
-                const i = vals.findIndex(val => keyframes[val])
-                if (i !== -1) {
-                  vals.splice(i, 1, keyframes[vals[i]])
-                  return vals.join(' ')
-                } else {
-                  return v
-                }
-              })
-              .join(',')
-          }
-        })
-      }
-    }
-  }
-}
-
-const processedRules = new WeakSet<Rule>()
-
-function processRule(id: string, rule: Rule) {
-  if (
-    processedRules.has(rule) ||
-    (rule.parent &&
-      rule.parent.type === 'atrule' &&
-      /-?keyframes$/.test((rule.parent as AtRule).name))
-  ) {
-    return
-  }
-  processedRules.add(rule)
-  rule.selector = selectorParser(selectorRoot => {
-    selectorRoot.each(selector => {
-      rewriteSelector(id, selector, selectorRoot)
-    })
-  }).processSync(rule.selector)
-}
-
-function rewriteSelector(
-  id: string,
-  selector: selectorParser.Selector,
-  selectorRoot: selectorParser.Root
-) {
-  let node: selectorParser.Node | null = null
-  let shouldInject = true
-  // find the last child node to insert attribute selector
-  selector.each(n => {
-    // DEPRECATED ">>>" and "/deep/" combinator
-    if (
-      n.type === 'combinator' &&
-      (n.value === '>>>' || n.value === '/deep/')
-    ) {
-      n.value = ' '
-      n.spaces.before = n.spaces.after = ''
-      // warn(
-      //   `the >>> and /deep/ combinators have been deprecated. ` +
-      //     `Use :deep() instead.`
-      // )
-      return false
-    }
-
-    if (n.type === 'pseudo') {
-      const { value } = n
-      // deep: inject [id] attribute at the node before the ::v-deep
-      // combinator.
-      if (value === ':deep' || value === '::v-deep') {
-        if (n.nodes.length) {
-          // .foo ::v-deep(.bar) -> .foo[xxxxxxx] .bar
-          // replace the current node with ::v-deep's inner selector
-          let last: selectorParser.Selector['nodes'][0] = n
-          n.nodes[0].each(ss => {
-            selector.insertAfter(last, ss)
-            last = ss
-          })
-          // insert a space combinator before if it doesn't already have one
-          const prev = selector.at(selector.index(n) - 1)
-          if (!prev || !isSpaceCombinator(prev)) {
-            selector.insertAfter(
-              n,
-              selectorParser.combinator({
-                value: ' '
-              })
-            )
-          }
-          selector.removeChild(n)
-        } else {
-          // DEPRECATED usage in v3
-          // .foo ::v-deep .bar -> .foo[xxxxxxx] .bar
-          // warn(
-          //   `::v-deep usage as a combinator has ` +
-          //     `been deprecated. Use :deep(<inner-selector>) instead.`
-          // )
-          const prev = selector.at(selector.index(n) - 1)
-          if (prev && isSpaceCombinator(prev)) {
-            selector.removeChild(prev)
-          }
-          selector.removeChild(n)
-        }
-        return false
-      }
-
-      // !!! Vue 2 does not have :slotted support
-      // ::v-slotted(.foo) -> .foo[xxxxxxx-s]
-      // if (value === ':slotted' || value === '::v-slotted') {
-      //   rewriteSelector(id, n.nodes[0], selectorRoot, true /* slotted */)
-      //   let last: selectorParser.Selector['nodes'][0] = n
-      //   n.nodes[0].each(ss => {
-      //     selector.insertAfter(last, ss)
-      //     last = ss
-      //   })
-      //   // selector.insertAfter(n, n.nodes[0])
-      //   selector.removeChild(n)
-      //   // since slotted attribute already scopes the selector there's no
-      //   // need for the non-slot attribute.
-      //   shouldInject = false
-      //   return false
-      // }
-
-      // global: replace with inner selector and do not inject [id].
-      // ::v-global(.foo) -> .foo
-      if (value === ':global' || value === '::v-global') {
-        selectorRoot.insertAfter(selector, n.nodes[0])
-        selectorRoot.removeChild(selector)
-        return false
-      }
-    }
-
-    if (n.type !== 'pseudo' && n.type !== 'combinator') {
-      node = n
-    }
-  })
-
-  if (node) {
-    ;(node as selectorParser.Node).spaces.after = ''
-  } else {
-    // For deep selectors & standalone pseudo selectors,
-    // the attribute selectors are prepended rather than appended.
-    // So all leading spaces must be eliminated to avoid problems.
-    selector.first.spaces.before = ''
-  }
-
-  if (shouldInject) {
-    selector.insertAfter(
-      // If node is null it means we need to inject [id] at the start
-      // insertAfter can handle `null` here
-      node as any,
-      selectorParser.attribute({
-        attribute: id,
-        value: id,
-        raws: {},
-        quoteMark: `"`
-      })
-    )
-  }
-}
-
-function isSpaceCombinator(node: selectorParser.Node) {
-  return node.type === 'combinator' && /^\s+$/.test(node.value)
-}
-
-scopedPlugin.postcss = true
-export default scopedPlugin
diff --git a/packages/compiler-sfc/src/stylePlugins/trim.ts b/packages/compiler-sfc/src/stylePlugins/trim.ts
deleted file mode 100644
index 67c4a3f0f35..00000000000
--- a/packages/compiler-sfc/src/stylePlugins/trim.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { PluginCreator } from 'postcss'
-
-const trimPlugin: PluginCreator<{}> = () => {
-  return {
-    postcssPlugin: 'vue-sfc-trim',
-    Once(root) {
-      root.walk(({ type, raws }) => {
-        if (type === 'rule' || type === 'atrule') {
-          if (raws.before) raws.before = '\n'
-          if ('after' in raws && raws.after) raws.after = '\n'
-        }
-      })
-    }
-  }
-}
-
-trimPlugin.postcss = true
-export default trimPlugin
diff --git a/packages/compiler-sfc/src/stylePreprocessors.ts b/packages/compiler-sfc/src/stylePreprocessors.ts
deleted file mode 100644
index b59ea302e72..00000000000
--- a/packages/compiler-sfc/src/stylePreprocessors.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-import merge from 'merge-source-map'
-import { RawSourceMap } from 'source-map'
-import { isFunction } from 'shared/util'
-
-export type StylePreprocessor = (
-  source: string,
-  map: RawSourceMap | undefined,
-  options: {
-    [key: string]: any
-    additionalData?: string | ((source: string, filename: string) => string)
-    filename: string
-  }
-) => StylePreprocessorResults
-
-export interface StylePreprocessorResults {
-  code: string
-  map?: object
-  errors: Error[]
-  dependencies: string[]
-}
-
-// .scss/.sass processor
-const scss: StylePreprocessor = (source, map, options) => {
-  const nodeSass = require('sass')
-  const finalOptions = {
-    ...options,
-    data: getSource(source, options.filename, options.additionalData),
-    file: options.filename,
-    outFile: options.filename,
-    sourceMap: !!map
-  }
-
-  try {
-    const result = nodeSass.renderSync(finalOptions)
-    const dependencies = result.stats.includedFiles
-    if (map) {
-      return {
-        code: result.css.toString(),
-        map: merge(map, JSON.parse(result.map.toString())),
-        errors: [],
-        dependencies
-      }
-    }
-
-    return { code: result.css.toString(), errors: [], dependencies }
-  } catch (e: any) {
-    return { code: '', errors: [e], dependencies: [] }
-  }
-}
-
-const sass: StylePreprocessor = (source, map, options) =>
-  scss(source, map, {
-    ...options,
-    indentedSyntax: true
-  })
-
-// .less
-const less: StylePreprocessor = (source, map, options) => {
-  const nodeLess = require('less')
-
-  let result: any
-  let error: Error | null = null
-  nodeLess.render(
-    getSource(source, options.filename, options.additionalData),
-    { ...options, syncImport: true },
-    (err: Error | null, output: any) => {
-      error = err
-      result = output
-    }
-  )
-
-  if (error) return { code: '', errors: [error], dependencies: [] }
-  const dependencies = result.imports
-  if (map) {
-    return {
-      code: result.css.toString(),
-      map: merge(map, result.map),
-      errors: [],
-      dependencies: dependencies
-    }
-  }
-
-  return {
-    code: result.css.toString(),
-    errors: [],
-    dependencies: dependencies
-  }
-}
-
-// .styl
-const styl: StylePreprocessor = (source, map, options) => {
-  const nodeStylus = require('stylus')
-  try {
-    const ref = nodeStylus(source)
-    Object.keys(options).forEach(key => ref.set(key, options[key]))
-    if (map) ref.set('sourcemap', { inline: false, comment: false })
-
-    const result = ref.render()
-    const dependencies = ref.deps()
-    if (map) {
-      return {
-        code: result,
-        map: merge(map, ref.sourcemap),
-        errors: [],
-        dependencies
-      }
-    }
-
-    return { code: result, errors: [], dependencies }
-  } catch (e: any) {
-    return { code: '', errors: [e], dependencies: [] }
-  }
-}
-
-function getSource(
-  source: string,
-  filename: string,
-  additionalData?: string | ((source: string, filename: string) => string)
-) {
-  if (!additionalData) return source
-  if (isFunction(additionalData)) {
-    return additionalData(source, filename)
-  }
-  return additionalData + source
-}
-
-export type PreprocessLang = 'less' | 'sass' | 'scss' | 'styl' | 'stylus'
-
-export const processors: Record<PreprocessLang, StylePreprocessor> = {
-  less,
-  sass,
-  scss,
-  styl,
-  stylus: styl
-}
diff --git a/packages/compiler-sfc/src/templateCompilerModules/assetUrl.ts b/packages/compiler-sfc/src/templateCompilerModules/assetUrl.ts
deleted file mode 100644
index 71e3cfa2fe9..00000000000
--- a/packages/compiler-sfc/src/templateCompilerModules/assetUrl.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-// vue compiler module for transforming `<tag>:<attribute>` to `require`
-
-import { urlToRequire } from './utils'
-import { ASTNode, ASTAttr } from 'types/compiler'
-
-export interface AssetURLOptions {
-  [name: string]: string | string[]
-}
-
-export interface TransformAssetUrlsOptions {
-  /**
-   * If base is provided, instead of transforming relative asset urls into
-   * imports, they will be directly rewritten to absolute urls.
-   */
-  base?: string
-  /**
-   * If true, also processes absolute urls.
-   */
-  includeAbsolute?: boolean
-}
-
-const defaultOptions: AssetURLOptions = {
-  audio: 'src',
-  video: ['src', 'poster'],
-  source: 'src',
-  img: 'src',
-  image: ['xlink:href', 'href'],
-  use: ['xlink:href', 'href']
-}
-
-export default (
-  userOptions?: AssetURLOptions,
-  transformAssetUrlsOption?: TransformAssetUrlsOptions
-) => {
-  const options = userOptions
-    ? Object.assign({}, defaultOptions, userOptions)
-    : defaultOptions
-
-  return {
-    postTransformNode: (node: ASTNode) => {
-      transform(node, options, transformAssetUrlsOption)
-    }
-  }
-}
-
-function transform(
-  node: ASTNode,
-  options: AssetURLOptions,
-  transformAssetUrlsOption?: TransformAssetUrlsOptions
-) {
-  if (node.type !== 1 || !node.attrs) return
-  for (const tag in options) {
-    if (tag === '*' || node.tag === tag) {
-      const attributes = options[tag]
-      if (typeof attributes === 'string') {
-        node.attrs!.some(attr =>
-          rewrite(attr, attributes, transformAssetUrlsOption)
-        )
-      } else if (Array.isArray(attributes)) {
-        attributes.forEach(item =>
-          node.attrs!.some(attr =>
-            rewrite(attr, item, transformAssetUrlsOption)
-          )
-        )
-      }
-    }
-  }
-}
-
-function rewrite(
-  attr: ASTAttr,
-  name: string,
-  transformAssetUrlsOption?: TransformAssetUrlsOptions
-) {
-  if (attr.name === name) {
-    const value = attr.value
-    // only transform static URLs
-    if (value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') {
-      attr.value = urlToRequire(value.slice(1, -1), transformAssetUrlsOption)
-      return true
-    }
-  }
-  return false
-}
diff --git a/packages/compiler-sfc/src/templateCompilerModules/srcset.ts b/packages/compiler-sfc/src/templateCompilerModules/srcset.ts
deleted file mode 100644
index bec3dcd7847..00000000000
--- a/packages/compiler-sfc/src/templateCompilerModules/srcset.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-// vue compiler module for transforming `img:srcset` to a number of `require`s
-
-import { urlToRequire } from './utils'
-import { TransformAssetUrlsOptions } from './assetUrl'
-import { ASTNode } from 'types/compiler'
-
-interface ImageCandidate {
-  require: string
-  descriptor: string
-}
-
-export default (transformAssetUrlsOptions?: TransformAssetUrlsOptions) => ({
-  postTransformNode: (node: ASTNode) => {
-    transform(node, transformAssetUrlsOptions)
-  }
-})
-
-// http://w3c.github.io/html/semantics-embedded-content.html#ref-for-image-candidate-string-5
-const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g
-
-function transform(
-  node: ASTNode,
-  transformAssetUrlsOptions?: TransformAssetUrlsOptions
-) {
-  if (node.type !== 1 || !node.attrs) {
-    return
-  }
-
-  if (node.tag === 'img' || node.tag === 'source') {
-    node.attrs.forEach(attr => {
-      if (attr.name === 'srcset') {
-        // same logic as in transform-require.js
-        const value = attr.value
-        const isStatic =
-          value.charAt(0) === '"' && value.charAt(value.length - 1) === '"'
-        if (!isStatic) {
-          return
-        }
-
-        const imageCandidates: ImageCandidate[] = value
-          .slice(1, -1)
-          .split(',')
-          .map(s => {
-            // The attribute value arrives here with all whitespace, except
-            // normal spaces, represented by escape sequences
-            const [url, descriptor] = s
-              .replace(escapedSpaceCharacters, ' ')
-              .trim()
-              .split(' ', 2)
-            return {
-              require: urlToRequire(url, transformAssetUrlsOptions),
-              descriptor
-            }
-          })
-
-        // "require(url1)"
-        // "require(url1) 1x"
-        // "require(url1), require(url2)"
-        // "require(url1), require(url2) 2x"
-        // "require(url1) 1x, require(url2)"
-        // "require(url1) 1x, require(url2) 2x"
-        const code = imageCandidates
-          .map(
-            ({ require, descriptor }) =>
-              `${require} + "${descriptor ? ' ' + descriptor : ''}, " + `
-          )
-          .join('')
-          .slice(0, -6)
-          .concat('"')
-          .replace(/ \+ ""$/, '')
-
-        attr.value = code
-      }
-    })
-  }
-}
diff --git a/packages/compiler-sfc/src/templateCompilerModules/utils.ts b/packages/compiler-sfc/src/templateCompilerModules/utils.ts
deleted file mode 100644
index 8a2d19c6ce7..00000000000
--- a/packages/compiler-sfc/src/templateCompilerModules/utils.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { TransformAssetUrlsOptions } from './assetUrl'
-import { UrlWithStringQuery, parse as uriParse } from 'url'
-import path from 'path'
-
-export function urlToRequire(
-  url: string,
-  transformAssetUrlsOption: TransformAssetUrlsOptions = {}
-): string {
-  const returnValue = `"${url}"`
-  // same logic as in transform-require.js
-  const firstChar = url.charAt(0)
-  if (firstChar === '~') {
-    const secondChar = url.charAt(1)
-    url = url.slice(secondChar === '/' ? 2 : 1)
-  }
-
-  if (isExternalUrl(url) || isDataUrl(url) || firstChar === '#') {
-    return returnValue
-  }
-
-  const uriParts = parseUriParts(url)
-  if (transformAssetUrlsOption.base) {
-    // explicit base - directly rewrite the url into absolute url
-    // does not apply to absolute urls or urls that start with `@`
-    // since they are aliases
-    if (firstChar === '.' || firstChar === '~') {
-      // Allow for full hostnames provided in options.base
-      const base = parseUriParts(transformAssetUrlsOption.base)
-      const protocol = base.protocol || ''
-      const host = base.host ? protocol + '//' + base.host : ''
-      const basePath = base.path || '/'
-      // when packaged in the browser, path will be using the posix-
-      // only version provided by rollup-plugin-node-builtins.
-      return `"${host}${(path.posix || path).join(
-        basePath,
-        uriParts.path + (uriParts.hash || '')
-      )}"`
-    }
-  }
-
-  if (
-    transformAssetUrlsOption.includeAbsolute ||
-    firstChar === '.' ||
-    firstChar === '~' ||
-    firstChar === '@'
-  ) {
-    if (!uriParts.hash) {
-      return `require("${url}")`
-    } else {
-      // support uri fragment case by excluding it from
-      // the require and instead appending it as string;
-      // assuming that the path part is sufficient according to
-      // the above caseing(t.i. no protocol-auth-host parts expected)
-      return `require("${uriParts.path}") + "${uriParts.hash}"`
-    }
-  }
-  return returnValue
-}
-
-/**
- * vuejs/component-compiler-utils#22 Support uri fragment in transformed require
- * @param urlString an url as a string
- */
-function parseUriParts(urlString: string): UrlWithStringQuery {
-  // initialize return value
-  const returnValue: UrlWithStringQuery = uriParse('')
-  if (urlString) {
-    // A TypeError is thrown if urlString is not a string
-    // @see https://nodejs.org/api/url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost
-    if ('string' === typeof urlString) {
-      // check is an uri
-      return uriParse(urlString, false, true) // take apart the uri
-    }
-  }
-  return returnValue
-}
-
-const externalRE = /^(https?:)?\/\//
-function isExternalUrl(url: string): boolean {
-  return externalRE.test(url)
-}
-
-const dataUrlRE = /^\s*data:/i
-function isDataUrl(url: string): boolean {
-  return dataUrlRE.test(url)
-}
diff --git a/packages/compiler-sfc/src/types.ts b/packages/compiler-sfc/src/types.ts
deleted file mode 100644
index c62f74c7bb7..00000000000
--- a/packages/compiler-sfc/src/types.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { CompilerOptions, CompiledResult } from 'types/compiler'
-import { SFCDescriptor } from './parseComponent'
-
-export interface StartOfSourceMap {
-  file?: string
-  sourceRoot?: string
-}
-
-export interface RawSourceMap extends StartOfSourceMap {
-  version: string
-  sources: string[]
-  names: string[]
-  sourcesContent?: string[]
-  mappings: string
-}
-
-export interface TemplateCompiler {
-  parseComponent(source: string, options?: any): SFCDescriptor
-  compile(template: string, options: CompilerOptions): CompiledResult
-  ssrCompile(template: string, options: CompilerOptions): CompiledResult
-}
-
-export const enum BindingTypes {
-  /**
-   * returned from data()
-   */
-  DATA = 'data',
-  /**
-   * declared as a prop
-   */
-  PROPS = 'props',
-  /**
-   * a local alias of a `<script setup>` destructured prop.
-   * the original is stored in __propsAliases of the bindingMetadata object.
-   */
-  PROPS_ALIASED = 'props-aliased',
-  /**
-   * a let binding (may or may not be a ref)
-   */
-  SETUP_LET = 'setup-let',
-  /**
-   * a const binding that can never be a ref.
-   * these bindings don't need `unref()` calls when processed in inlined
-   * template expressions.
-   */
-  SETUP_CONST = 'setup-const',
-  /**
-   * a const binding that does not need `unref()`, but may be mutated.
-   */
-  SETUP_REACTIVE_CONST = 'setup-reactive-const',
-  /**
-   * a const binding that may be a ref.
-   */
-  SETUP_MAYBE_REF = 'setup-maybe-ref',
-  /**
-   * bindings that are guaranteed to be refs
-   */
-  SETUP_REF = 'setup-ref',
-  /**
-   * declared by other options, e.g. computed, inject
-   */
-  OPTIONS = 'options'
-}
-
-export type BindingMetadata = {
-  [key: string]: BindingTypes | undefined
-} & {
-  __isScriptSetup?: boolean
-}
diff --git a/packages/compiler-sfc/src/warn.ts b/packages/compiler-sfc/src/warn.ts
deleted file mode 100644
index e4b698dc4f5..00000000000
--- a/packages/compiler-sfc/src/warn.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-const hasWarned: Record<string, boolean> = {}
-
-export function warnOnce(msg: string) {
-  const isNodeProd =
-    typeof process !== 'undefined' && process.env.NODE_ENV === 'production'
-  if (!isNodeProd && !hasWarned[msg]) {
-    hasWarned[msg] = true
-    warn(msg)
-  }
-}
-
-export function warn(msg: string) {
-  console.warn(
-    `\x1b[1m\x1b[33m[@vue/compiler-sfc]\x1b[0m\x1b[33m ${msg}\x1b[0m\n`
-  )
-}
diff --git a/packages/compiler-sfc/test/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/test/__snapshots__/compileScript.spec.ts.snap
deleted file mode 100644
index 4b81610a48d..00000000000
--- a/packages/compiler-sfc/test/__snapshots__/compileScript.spec.ts.snap
+++ /dev/null
@@ -1,971 +0,0 @@
-// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-
-exports[`SFC analyze <script> bindings > auto name inference > basic 1`] = `
-"export default {
-  __name: 'FooBar',
-  setup(__props) {
-const a = 1
-return { a }
-}
-
-}"
-`;
-
-exports[`SFC analyze <script> bindings > auto name inference > do not overwrite manual name (call) 1`] = `
-"import { defineComponent } from 'vue'
-        const __default__ = defineComponent({
-          name: 'Baz'
-        })
-        
-export default /*#__PURE__*/Object.assign(__default__, {
-  setup(__props) {
-const a = 1
-return { a }
-}
-
-})"
-`;
-
-exports[`SFC analyze <script> bindings > auto name inference > do not overwrite manual name (object) 1`] = `
-"const __default__ = {
-          name: 'Baz'
-        }
-        
-export default /*#__PURE__*/Object.assign(__default__, {
-  setup(__props) {
-const a = 1
-return { a }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > <script> after <script setup> the script content not end with \`\\n\` 1`] = `
-"const n = 1
-import { x } from './x'
-    
-export default {
-  setup(__props) {
-
-    
-return { n, x }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script first 1`] = `
-"import { x } from './x'
-      
-      export const n = 1
-
-      const __default__ = {}
-      
-export default /*#__PURE__*/Object.assign(__default__, {
-  setup(__props) {
-
-      x()
-      
-return { n, x }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script setup first 1`] = `
-"export const n = 1
-      const __default__ = {}
-      
-import { x } from './x'
-      
-export default /*#__PURE__*/Object.assign(__default__, {
-  setup(__props) {
-
-      x()
-      
-return { n, x }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script setup first, lang="ts", script block content export default 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-
-      const __default__ = {
-        name: "test"
-      }
-      
-import { x } from './x'
-      
-export default /*#__PURE__*/_defineComponent({
-  ...__default__,
-  setup(__props) {
-
-      x()
-      
-return { x }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script setup first, named default export 1`] = `
-"export const n = 1
-      const def = {}
-      
-      
-const __default__ = def
-
-import { x } from './x'
-      
-export default /*#__PURE__*/Object.assign(__default__, {
-  setup(__props) {
-
-      x()
-      
-return { n, def, x }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > <script> and <script setup> co-usage > spaces in ExportDefaultDeclaration node > with many spaces and newline 1`] = `
-"import { x } from './x'
-        
-        export const n = 1
-        const __default__ = {
-          some:'option'
-        }
-        
-export default /*#__PURE__*/Object.assign(__default__, {
-  setup(__props) {
-
-        x()
-        
-return { n, x }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > <script> and <script setup> co-usage > spaces in ExportDefaultDeclaration node > with minimal spaces 1`] = `
-"import { x } from './x'
-        
-        export const n = 1
-        const __default__ = {
-          some:'option'
-        }
-        
-export default /*#__PURE__*/Object.assign(__default__, {
-  setup(__props) {
-
-        x()
-        
-return { n, x }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > binding analysis for destructure 1`] = `
-"export default {
-  setup(__props) {
-
-      const { foo, b: bar, ['x' + 'y']: baz, x: { y, zz: { z }}} = {}
-      
-return { foo, bar, baz, y, z }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > defineEmits() 1`] = `
-"export default {
-  emits: ['foo', 'bar'],
-  setup(__props, { emit: myEmit }) {
-
-
-
-return { myEmit }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > defineExpose() 1`] = `
-"export default {
-  setup(__props, { expose }) {
-
-expose({ foo: 123 })
-
-return {  }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > defineProps w/ external definition 1`] = `
-"import { propsModel } from './props'
-    
-export default {
-  props: propsModel,
-  setup(__props) {
-
-const props = __props;
-
-    
-    
-return { props, propsModel }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > defineProps w/ leading code 1`] = `
-"import { x } from './x'
-    
-export default {
-  props: {},
-  setup(__props) {
-
-const props = __props;
-
-    
-return { props, x }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > defineProps() 1`] = `
-"export default {
-  props: {
-  foo: String
-},
-  setup(__props) {
-
-const props = __props;
-
-
-const bar = 1
-
-return { props, bar }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > defineProps/defineEmits in multi-variable declaration (full removal) 1`] = `
-"export default {
-  props: ['item'],
-  emits: ['a'],
-  setup(__props, { emit }) {
-
-const props = __props;
-
-    
-    
-return { props, emit }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > defineProps/defineEmits in multi-variable declaration 1`] = `
-"export default {
-  props: ['item'],
-  emits: ['a'],
-  setup(__props, { emit }) {
-
-const props = __props;
-
-    const a = 1;
-    
-return { props, a, emit }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > defineProps/defineEmits in multi-variable declaration fix #6757  1`] = `
-"export default {
-  props: ['item'],
-  emits: ['a'],
-  setup(__props, { emit }) {
-
-const props = __props;
-
-    const a = 1;
-    
-return { a, props, emit }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > TS annotations 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { Foo, Baz, Qux, Fred } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        const a = 1
-        function b() {}
-        
-return { a, b, Baz }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > attribute expressions 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { bar, baz } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        const cond = true
-        
-return { cond, bar, baz }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > components 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { FooBar, FooBaz, FooQux, foo } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        const fooBar: FooBar = 1
-        
-return { fooBar, FooBaz, FooQux, foo }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > directive 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { vMyDir } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        
-return { vMyDir }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > imported ref as template ref 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { aref } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        
-return { aref }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > js template string interpolations 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { VAR, VAR2, VAR3 } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        
-return { VAR, VAR3 }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > last tag 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { FooBaz, Last } from './x'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        
-return { FooBaz, Last }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > dev mode import usage check > vue interpolations 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import { x, y, z, x$y } from './x'
-      
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-      
-return { x, z, x$y }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > errors > should allow defineProps/Emit() referencing imported binding 1`] = `
-"import { bar } from './bar'
-        
-export default {
-  props: {
-          foo: {
-            default: () => bar
-          }
-        },
-  emits: {
-          foo: () => bar > 1
-        },
-  setup(__props) {
-
-        
-        
-        
-return { bar }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > errors > should allow defineProps/Emit() referencing scope var 1`] = `
-"export default {
-  props: {
-            foo: {
-              default: bar => bar + 1
-            }
-          },
-  emits: {
-            foo: bar => bar > 1
-          },
-  setup(__props) {
-
-          const bar = 1
-          
-          
-        
-return { bar }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > imports > import dedupe between <script> and <script setup> 1`] = `
-"import { x } from './x'
-        
-export default {
-  setup(__props) {
-
-        x()
-        
-return { x }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > imports > should allow defineProps/Emit at the start of imports 1`] = `
-"import { ref } from 'vue'
-      
-export default {
-  props: ['foo'],
-  emits: ['bar'],
-  setup(__props) {
-
-      
-      
-      const r = ref(0)
-      
-return { r, ref }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > imports > should extract comment for import or type declarations 1`] = `
-"import a from 'a' // comment
-        import b from 'b'
-        
-export default {
-  setup(__props) {
-
-        
-return { a, b }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > imports > should hoist and expose imports 1`] = `
-"import { ref } from 'vue'
-          import 'foo/css'
-        
-export default {
-  setup(__props) {
-
-          
-return { ref }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > should expose top level declarations 1`] = `
-"import { xx } from './x'
-      let aa = 1
-      const bb = 2
-      function cc() {}
-      class dd {}
-      
-import { x } from './x'
-      
-export default {
-  setup(__props) {
-
-      let a = 1
-      const b = 2
-      function c() {}
-      class d {}
-      
-return { aa, bb, cc, dd, a, b, c, d, xx, x }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > const Enum 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-const enum Foo { A = 123 }
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        
-return { Foo }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (exported interface) 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-export interface Emits { (e: 'foo' | 'bar'): void }
-      
-export default /*#__PURE__*/_defineComponent({
-  emits: ["foo", "bar"],
-  setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
-
-      
-      
-return { emit }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (exported type alias) 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-export type Emits = { (e: 'foo' | 'bar'): void }
-      
-export default /*#__PURE__*/_defineComponent({
-  emits: ["foo", "bar"],
-  setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
-
-      
-      
-return { emit }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (interface ts type) 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-interface Emits { (e: 'foo'): void }
-      
-export default /*#__PURE__*/_defineComponent({
-  emits: ['foo'],
-  setup(__props, { emit }) {
-
-      
-      
-return { emit }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (interface) 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-interface Emits { (e: 'foo' | 'bar'): void }
-      
-export default /*#__PURE__*/_defineComponent({
-  emits: ["foo", "bar"],
-  setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
-
-      
-      
-return { emit }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (referenced exported function type) 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-export type Emits = (e: 'foo' | 'bar') => void
-      
-export default /*#__PURE__*/_defineComponent({
-  emits: ["foo", "bar"],
-  setup(__props, { emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
-
-      
-      
-return { emit }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (referenced function type) 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-type Emits = (e: 'foo' | 'bar') => void
-      
-export default /*#__PURE__*/_defineComponent({
-  emits: ["foo", "bar"],
-  setup(__props, { emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
-
-      
-      
-return { emit }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (type alias) 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-type Emits = { (e: 'foo' | 'bar'): void }
-      
-export default /*#__PURE__*/_defineComponent({
-  emits: ["foo", "bar"],
-  setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
-
-      
-      
-return { emit }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (type literal w/ call signatures) 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-
-export default /*#__PURE__*/_defineComponent({
-  emits: ["foo", "bar", "baz"],
-  setup(__props, { emit }: { emit: ({(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}), expose: any, slots: any, attrs: any }) {
-
-      
-      
-return { emit }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-
-export default /*#__PURE__*/_defineComponent({
-  emits: ["foo", "bar"],
-  setup(__props, { emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
-
-      
-      
-return { emit }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineProps w/ exported interface 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-export interface Props { x?: number }
-      
-export default /*#__PURE__*/_defineComponent({
-  props: {
-    x: { type: Number, required: false }
-  },
-  setup(__props: any) {
-
-      
-      
-return {  }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineProps w/ exported interface in normal script 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-
-        export interface Props { x?: number }
-      
-export default /*#__PURE__*/_defineComponent({
-  props: {
-    x: { type: Number, required: false }
-  },
-  setup(__props: any) {
-
-        
-      
-return {  }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineProps w/ exported type alias 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-export type Props = { x?: number }
-      
-export default /*#__PURE__*/_defineComponent({
-  props: {
-    x: { type: Number, required: false }
-  },
-  setup(__props: any) {
-
-      
-      
-return {  }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineProps w/ interface 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-interface Props { x?: number }
-      
-export default /*#__PURE__*/_defineComponent({
-  props: {
-    x: { type: Number, required: false }
-  },
-  setup(__props: any) {
-
-      
-      
-return {  }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineProps w/ type 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-interface Test {}
-
-      type Alias = number[]
-
-      
-export default /*#__PURE__*/_defineComponent({
-  props: {
-    string: { type: String, required: true },
-    number: { type: Number, required: true },
-    boolean: { type: Boolean, required: true },
-    object: { type: Object, required: true },
-    objectLiteral: { type: Object, required: true },
-    fn: { type: Function, required: true },
-    functionRef: { type: Function, required: true },
-    objectRef: { type: Object, required: true },
-    dateTime: { type: Date, required: true },
-    array: { type: Array, required: true },
-    arrayRef: { type: Array, required: true },
-    tuple: { type: Array, required: true },
-    set: { type: Set, required: true },
-    literal: { type: String, required: true },
-    optional: { type: null, required: false },
-    recordRef: { type: Object, required: true },
-    interface: { type: Object, required: true },
-    alias: { type: Array, required: true },
-    method: { type: Function, required: true },
-    symbol: { type: Symbol, required: true },
-    union: { type: [String, Number], required: true },
-    literalUnion: { type: String, required: true },
-    literalUnionNumber: { type: Number, required: true },
-    literalUnionMixed: { type: [String, Number, Boolean], required: true },
-    intersection: { type: Object, required: true },
-    foo: { type: [Function, null], required: true }
-  },
-  setup(__props: any) {
-
-      
-      
-return {  }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineProps w/ type alias 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-type Props = { x?: number }
-      
-export default /*#__PURE__*/_defineComponent({
-  props: {
-    x: { type: Number, required: false }
-  },
-  setup(__props: any) {
-
-      
-      
-return {  }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > defineProps/Emit w/ runtime options 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-
-export default /*#__PURE__*/_defineComponent({
-  props: { foo: String },
-  emits: ['a', 'b'],
-  setup(__props, { emit }) {
-
-const props = __props;
-
-
-
-
-return { props, emit }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > hoist type declarations 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-export interface Foo {}
-        type Bar = {}
-      
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        
-return {  }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > import type 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-import type { Foo } from './main.ts'
-        import { type Bar, Baz } from './main.ts'
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        
-return { Baz }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > runtime Enum 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-enum Foo { A = 123 }
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        
-return { Foo }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > runtime Enum in normal script 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-enum Foo { A = 123 }
-        
-          export enum D { D = "D" }
-          const enum C { C = "C" }
-          enum B { B = "B" }
-        
-export default /*#__PURE__*/_defineComponent({
-  setup(__props) {
-
-        
-return { D, C, B, Foo }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > withDefaults (dynamic) 1`] = `
-"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
-import { defaults } from './foo'
-      
-export default /*#__PURE__*/_defineComponent({
-  props: _mergeDefaults({
-    foo: { type: String, required: false },
-    bar: { type: Number, required: false },
-    baz: { type: Boolean, required: true }
-  }, { ...defaults }),
-  setup(__props: any) {
-
-const props = __props as {
-        foo?: string
-        bar?: number
-        baz: boolean
-      };
-
-      
-      
-return { props, defaults }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> > with TypeScript > withDefaults (static) 1`] = `
-"import { defineComponent as _defineComponent } from 'vue'
-
-export default /*#__PURE__*/_defineComponent({
-  props: {
-    foo: { type: String, required: false, default: 'hi' },
-    bar: { type: Number, required: false },
-    baz: { type: Boolean, required: true },
-    qux: { type: Function, required: false, default() { return 1 } }
-  },
-  setup(__props: any) {
-
-const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number };
-
-      
-      
-return { props }
-}
-
-})"
-`;
diff --git a/packages/compiler-sfc/test/__snapshots__/cssVars.spec.ts.snap b/packages/compiler-sfc/test/__snapshots__/cssVars.spec.ts.snap
deleted file mode 100644
index a9071ac1f98..00000000000
--- a/packages/compiler-sfc/test/__snapshots__/cssVars.spec.ts.snap
+++ /dev/null
@@ -1,189 +0,0 @@
-// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-
-exports[`CSS vars injection > codegen > <script> w/ default export 1`] = `
-"const __default__ = { setup() {} }
-import { useCssVars as _useCssVars } from 'vue'
-const __injectCSSVars__ = () => {
-_useCssVars((_vm, _setup) => ({
-  "xxxxxxxx-color": (_vm.color)
-}))}
-const __setup__ = __default__.setup
-__default__.setup = __setup__
-  ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
-  : __injectCSSVars__
-
-export default __default__"
-`;
-
-exports[`CSS vars injection > codegen > <script> w/ default export in strings/comments 1`] = `
-"
-          // export default {}
-          const __default__ = {}
-        
-import { useCssVars as _useCssVars } from 'vue'
-const __injectCSSVars__ = () => {
-_useCssVars((_vm, _setup) => ({
-  "xxxxxxxx-color": (_vm.color)
-}))}
-const __setup__ = __default__.setup
-__default__.setup = __setup__
-  ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
-  : __injectCSSVars__
-
-export default __default__"
-`;
-
-exports[`CSS vars injection > codegen > <script> w/ no default export 1`] = `
-"const a = 1
-const __default__ = {}
-import { useCssVars as _useCssVars } from 'vue'
-const __injectCSSVars__ = () => {
-_useCssVars((_vm, _setup) => ({
-  "xxxxxxxx-color": (_vm.color)
-}))}
-const __setup__ = __default__.setup
-__default__.setup = __setup__
-  ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
-  : __injectCSSVars__
-
-export default __default__"
-`;
-
-exports[`CSS vars injection > codegen > should ignore comments 1`] = `
-"import { useCssVars as _useCssVars } from 'vue'
-
-export default {
-  setup(__props) {
-
-_useCssVars((_vm, _setup) => ({
-  "xxxxxxxx-width": (_setup.width)
-}))
-const color = 'red';const width = 100
-return { color, width }
-}
-
-}"
-`;
-
-exports[`CSS vars injection > codegen > should work with w/ complex expression 1`] = `
-"import { useCssVars as _useCssVars } from 'vue'
-
-export default {
-  setup(__props) {
-
-_useCssVars((_vm, _setup) => ({
-  "xxxxxxxx-foo": (_setup.foo),
-  "xxxxxxxx-foo____px_": (_setup.foo + 'px'),
-  "xxxxxxxx-_a___b____2____px_": ((_setup.a + _setup.b) / 2 + 'px'),
-  "xxxxxxxx-__a___b______2___a_": (((_setup.a + _setup.b)) / (2 * _setup.a))
-}))
-
-        let a = 100
-        let b = 200
-        let foo = 300
-        
-return { a, b, foo }
-}
-
-}"
-`;
-
-exports[`CSS vars injection > codegen > w/ <script setup> 1`] = `
-"import { useCssVars as _useCssVars } from 'vue'
-
-export default {
-  setup(__props) {
-
-_useCssVars((_vm, _setup) => ({
-  "xxxxxxxx-color": (_setup.color)
-}))
-const color = 'red'
-return { color }
-}
-
-}"
-`;
-
-exports[`CSS vars injection > codegen > w/ <script setup> using the same var multiple times 1`] = `
-"import { useCssVars as _useCssVars } from 'vue'
-
-export default {
-  setup(__props) {
-
-_useCssVars((_vm, _setup) => ({
-  "xxxxxxxx-color": (_setup.color)
-}))
-
-        const color = 'red'
-        
-return { color }
-}
-
-}"
-`;
-
-exports[`CSS vars injection > generating correct code for nested paths 1`] = `
-"const a = 1
-const __default__ = {}
-import { useCssVars as _useCssVars } from 'vue'
-const __injectCSSVars__ = () => {
-_useCssVars((_vm, _setup) => ({
-  "xxxxxxxx-color": (_vm.color),
-  "xxxxxxxx-font_size": (_vm.font.size)
-}))}
-const __setup__ = __default__.setup
-__default__.setup = __setup__
-  ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
-  : __injectCSSVars__
-
-export default __default__"
-`;
-
-exports[`CSS vars injection > w/ <script setup> binding analysis 1`] = `
-"import { useCssVars as _useCssVars } from 'vue'
-import { ref } from 'vue'
-        
-export default {
-  props: {
-          foo: String
-        },
-  setup(__props) {
-
-_useCssVars((_vm, _setup) => ({
-  "xxxxxxxx-color": (_setup.color),
-  "xxxxxxxx-size": (_setup.size),
-  "xxxxxxxx-foo": (_vm.foo)
-}))
-
-        const color = 'red'
-        const size = ref('10px')
-        
-        
-return { color, size, ref }
-}
-
-}"
-`;
-
-exports[`CSS vars injection > w/ normal <script> binding analysis 1`] = `
-"
-      const __default__ = {
-        setup() {
-          return {
-            size: ref('100px')
-          }
-        }
-      }
-      
-import { useCssVars as _useCssVars } from 'vue'
-const __injectCSSVars__ = () => {
-_useCssVars((_vm, _setup) => ({
-  "xxxxxxxx-size": (_vm.size)
-}))}
-const __setup__ = __default__.setup
-__default__.setup = __setup__
-  ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
-  : __injectCSSVars__
-
-export default __default__"
-`;
diff --git a/packages/compiler-sfc/test/compileScript.spec.ts b/packages/compiler-sfc/test/compileScript.spec.ts
deleted file mode 100644
index 6e04789bfd7..00000000000
--- a/packages/compiler-sfc/test/compileScript.spec.ts
+++ /dev/null
@@ -1,1635 +0,0 @@
-import { BindingTypes } from '../src/types'
-import { compile, assertCode } from './util'
-
-describe('SFC compile <script setup>', () => {
-  test('should expose top level declarations', () => {
-    const { content, bindings } = compile(`
-      <script setup>
-      import { x } from './x'
-      let a = 1
-      const b = 2
-      function c() {}
-      class d {}
-      </script>
-
-      <script>
-      import { xx } from './x'
-      let aa = 1
-      const bb = 2
-      function cc() {}
-      class dd {}
-      </script>
-      `)
-    expect(content).toMatch('return { aa, bb, cc, dd, a, b, c, d, xx, x }')
-    expect(bindings).toStrictEqual({
-      x: BindingTypes.SETUP_MAYBE_REF,
-      a: BindingTypes.SETUP_LET,
-      b: BindingTypes.SETUP_CONST,
-      c: BindingTypes.SETUP_CONST,
-      d: BindingTypes.SETUP_CONST,
-      xx: BindingTypes.SETUP_MAYBE_REF,
-      aa: BindingTypes.SETUP_LET,
-      bb: BindingTypes.SETUP_CONST,
-      cc: BindingTypes.SETUP_CONST,
-      dd: BindingTypes.SETUP_CONST
-    })
-    assertCode(content)
-  })
-
-  test('binding analysis for destructure', () => {
-    const { content, bindings } = compile(`
-      <script setup>
-      const { foo, b: bar, ['x' + 'y']: baz, x: { y, zz: { z }}} = {}
-      </script>
-      `)
-    expect(content).toMatch('return { foo, bar, baz, y, z }')
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.SETUP_MAYBE_REF,
-      bar: BindingTypes.SETUP_MAYBE_REF,
-      baz: BindingTypes.SETUP_MAYBE_REF,
-      y: BindingTypes.SETUP_MAYBE_REF,
-      z: BindingTypes.SETUP_MAYBE_REF
-    })
-    assertCode(content)
-  })
-
-  test('defineProps()', () => {
-    const { content, bindings } = compile(`
-<script setup>
-const props = defineProps({
-  foo: String
-})
-const bar = 1
-</script>
-  `)
-    // should generate working code
-    assertCode(content)
-    // should analyze bindings
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.PROPS,
-      bar: BindingTypes.SETUP_CONST,
-      props: BindingTypes.SETUP_REACTIVE_CONST
-    })
-
-    // should remove defineOptions import and call
-    expect(content).not.toMatch('defineProps')
-    // should generate correct setup signature
-    expect(content).toMatch(`setup(__props) {`)
-    // should assign user identifier to it
-    expect(content).toMatch(`const props = __props`)
-    // should include context options in default export
-    expect(content).toMatch(`export default {
-  props: {
-  foo: String
-},`)
-  })
-
-  test('defineProps w/ external definition', () => {
-    const { content } = compile(`
-    <script setup>
-    import { propsModel } from './props'
-    const props = defineProps(propsModel)
-    </script>
-      `)
-    assertCode(content)
-    expect(content).toMatch(`export default {
-  props: propsModel,`)
-  })
-
-  // #4764
-  test('defineProps w/ leading code', () => {
-    const { content } = compile(`
-    <script setup>import { x } from './x'
-    const props = defineProps({})
-    </script>
-    `)
-    // props declaration should be inside setup, not moved along with the import
-    expect(content).not.toMatch(`const props = __props\nimport`)
-    assertCode(content)
-  })
-
-  test('defineEmits()', () => {
-    const { content, bindings } = compile(`
-<script setup>
-const myEmit = defineEmits(['foo', 'bar'])
-</script>
-  `)
-    assertCode(content)
-    expect(bindings).toStrictEqual({
-      myEmit: BindingTypes.SETUP_CONST
-    })
-    // should remove defineOptions import and call
-    expect(content).not.toMatch('defineEmits')
-    // should generate correct setup signature
-    expect(content).toMatch(`setup(__props, { emit: myEmit }) {`)
-    // should include context options in default export
-    expect(content).toMatch(`export default {
-  emits: ['foo', 'bar'],`)
-  })
-
-  test('defineProps/defineEmits in multi-variable declaration', () => {
-    const { content } = compile(`
-    <script setup>
-    const props = defineProps(['item']),
-      a = 1,
-      emit = defineEmits(['a']);
-    </script>
-  `)
-    assertCode(content)
-    expect(content).toMatch(`const a = 1;`) // test correct removal
-    expect(content).toMatch(`props: ['item'],`)
-    expect(content).toMatch(`emits: ['a'],`)
-  })
-
-  // vuejs/core #6757
-  test('defineProps/defineEmits in multi-variable declaration fix #6757 ', () => {
-    const { content } = compile(`
-    <script setup>
-    const a = 1,
-          props = defineProps(['item']),
-          emit = defineEmits(['a']);
-    </script>
-  `)
-    assertCode(content)
-    expect(content).toMatch(`const a = 1;`) // test correct removal
-    expect(content).toMatch(`props: ['item'],`)
-    expect(content).toMatch(`emits: ['a'],`)
-  })
-
-  test('defineProps/defineEmits in multi-variable declaration (full removal)', () => {
-    const { content } = compile(`
-    <script setup>
-    const props = defineProps(['item']),
-          emit = defineEmits(['a']);
-    </script>
-  `)
-    assertCode(content)
-    expect(content).toMatch(`props: ['item'],`)
-    expect(content).toMatch(`emits: ['a'],`)
-  })
-
-  test('defineExpose()', () => {
-    const { content } = compile(`
-<script setup>
-defineExpose({ foo: 123 })
-</script>
-  `)
-    assertCode(content)
-    // should remove defineOptions import and call
-    expect(content).not.toMatch('defineExpose')
-    // should generate correct setup signature
-    expect(content).toMatch(`setup(__props, { expose }) {`)
-    // should replace callee
-    expect(content).toMatch(/\bexpose\(\{ foo: 123 \}\)/)
-  })
-
-  test('<script> after <script setup> the script content not end with `\\n`', () => {
-    const { content } = compile(`
-    <script setup>
-    import { x } from './x'
-    </script>
-    <script>const n = 1</script>
-    `)
-    assertCode(content)
-  })
-
-  describe('<script> and <script setup> co-usage', () => {
-    test('script first', () => {
-      const { content } = compile(`
-      <script>
-      export const n = 1
-
-      export default {}
-      </script>
-      <script setup>
-      import { x } from './x'
-      x()
-      </script>
-      `)
-      assertCode(content)
-    })
-
-    test('script setup first', () => {
-      const { content } = compile(`
-      <script setup>
-      import { x } from './x'
-      x()
-      </script>
-      <script>
-      export const n = 1
-      export default {}
-      </script>
-      `)
-      assertCode(content)
-    })
-
-    test('script setup first, named default export', () => {
-      const { content } = compile(`
-      <script setup>
-      import { x } from './x'
-      x()
-      </script>
-      <script>
-      export const n = 1
-      const def = {}
-      export { def as default }
-      </script>
-      `)
-      assertCode(content)
-    })
-
-    // #4395
-    test('script setup first, lang="ts", script block content export default', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      import { x } from './x'
-      x()
-      </script>
-      <script lang="ts">
-      export default {
-        name: "test"
-      }
-      </script>
-      `)
-      // ensure __default__ is declared before used
-      expect(content).toMatch(/const __default__[\S\s]*\.\.\.__default__/m)
-      assertCode(content)
-    })
-
-    describe('spaces in ExportDefaultDeclaration node', () => {
-      // #4371
-      test('with many spaces and newline', () => {
-        // #4371
-        const { content } = compile(`
-        <script>
-        export const n = 1
-        export        default
-        {
-          some:'option'
-        }
-        </script>
-        <script setup>
-        import { x } from './x'
-        x()
-        </script>
-        `)
-        assertCode(content)
-      })
-
-      test('with minimal spaces', () => {
-        const { content } = compile(`
-        <script>
-        export const n = 1
-        export default{
-          some:'option'
-        }
-        </script>
-        <script setup>
-        import { x } from './x'
-        x()
-        </script>
-        `)
-        assertCode(content)
-      })
-    })
-  })
-
-  describe('imports', () => {
-    test('should hoist and expose imports', () => {
-      assertCode(
-        compile(`<script setup>
-          import { ref } from 'vue'
-          import 'foo/css'
-        </script>`).content
-      )
-    })
-
-    test('should extract comment for import or type declarations', () => {
-      assertCode(
-        compile(`
-        <script setup>
-        import a from 'a' // comment
-        import b from 'b'
-        </script>
-        `).content
-      )
-    })
-
-    // #2740
-    test('should allow defineProps/Emit at the start of imports', () => {
-      assertCode(
-        compile(`<script setup>
-      import { ref } from 'vue'
-      defineProps(['foo'])
-      defineEmits(['bar'])
-      const r = ref(0)
-      </script>`).content
-      )
-    })
-
-    test('import dedupe between <script> and <script setup>', () => {
-      const { content } = compile(`
-        <script>
-        import { x } from './x'
-        </script>
-        <script setup>
-        import { x } from './x'
-        x()
-        </script>
-        `)
-      assertCode(content)
-      expect(content.indexOf(`import { x }`)).toEqual(
-        content.lastIndexOf(`import { x }`)
-      )
-    })
-  })
-
-  // in dev mode, declared bindings are returned as an object from setup()
-  // when using TS, users may import types which should not be returned as
-  // values, so we need to check import usage in the template to determine
-  // what to be returned.
-  describe('dev mode import usage check', () => {
-    test('components', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { FooBar, FooBaz, FooQux, foo } from './x'
-        const fooBar: FooBar = 1
-        </script>
-        <template>
-          <FooBaz></FooBaz>
-          <foo-qux/>
-          <foo/>
-          FooBar
-        </template>
-        `)
-      // FooBar: should not be matched by plain text or incorrect case
-      // FooBaz: used as PascalCase component
-      // FooQux: used as kebab-case component
-      // foo: lowercase component
-      expect(content).toMatch(`return { fooBar, FooBaz, FooQux, foo }`)
-      assertCode(content)
-    })
-
-    test('directive', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { vMyDir } from './x'
-        </script>
-        <template>
-          <div v-my-dir></div>
-        </template>
-        `)
-      expect(content).toMatch(`return { vMyDir }`)
-      assertCode(content)
-    })
-
-    // https://github.com/vuejs/core/issues/4599
-    test('attribute expressions', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { bar, baz } from './x'
-        const cond = true
-        </script>
-        <template>
-          <div :class="[cond ? '' : bar(), 'default']" :style="baz"></div>
-        </template>
-        `)
-      expect(content).toMatch(`return { cond, bar, baz }`)
-      assertCode(content)
-    })
-
-    test('imported ref as template ref', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { aref } from './x'
-        </script>
-        <template>
-          <div ref="aref"></div>
-        </template>
-        `)
-      expect(content).toMatch(`return { aref }`)
-      assertCode(content)
-    })
-
-    test('vue interpolations', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      import { x, y, z, x$y } from './x'
-      </script>
-      <template>
-        <div :id="z + 'y'">{{ x }} {{ yy }} {{ x$y }}</div>
-      </template>
-      `)
-      // x: used in interpolation
-      // y: should not be matched by {{ yy }} or 'y' in binding exps
-      // x$y: #4274 should escape special chars when creating Regex
-      expect(content).toMatch(`return { x, z, x$y }`)
-      assertCode(content)
-    })
-
-    // #4340 interpolations in template strings
-    test('js template string interpolations', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { VAR, VAR2, VAR3 } from './x'
-        </script>
-        <template>
-          {{ \`\${VAR}VAR2\${VAR3}\` }}
-        </template>
-        `)
-      // VAR2 should not be matched
-      expect(content).toMatch(`return { VAR, VAR3 }`)
-      assertCode(content)
-    })
-
-    // edge case: last tag in template
-    test('last tag', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { FooBaz, Last } from './x'
-        </script>
-        <template>
-          <FooBaz></FooBaz>
-          <Last/>
-        </template>
-        `)
-      expect(content).toMatch(`return { FooBaz, Last }`)
-      assertCode(content)
-    })
-
-    test('TS annotations', () => {
-      const { content } = compile(`
-        <script setup lang="ts">
-        import { Foo, Baz, Qux, Fred } from './x'
-        const a = 1
-        function b() {}
-        </script>
-        <template>
-          {{ a as Foo }}
-          {{ Baz }}
-          <Comp v-slot="{ data }: Qux">{{ data }}</Comp>
-          <div v-for="{ z = x as Qux } in list as Fred"/>
-        </template>
-        `)
-
-      expect(content).toMatch(`return { a, b, Baz }`)
-      assertCode(content)
-    })
-  })
-
-  // describe('inlineTemplate mode', () => {
-  //   test('should work', () => {
-  //     const { content } = compile(
-  //       `
-  //       <script setup>
-  //       import { ref } from 'vue'
-  //       const count = ref(0)
-  //       </script>
-  //       <template>
-  //         <div>{{ count }}</div>
-  //         <div>static</div>
-  //       </template>
-  //       `,
-  //       { inlineTemplate: true }
-  //     )
-  //     // check snapshot and make sure helper imports and
-  //     // hoists are placed correctly.
-  //     assertCode(content)
-  //     // in inline mode, no need to call expose() since nothing is exposed
-  //     // anyway!
-  //     expect(content).not.toMatch(`expose()`)
-  //   })
-
-  //   test('with defineExpose()', () => {
-  //     const { content } = compile(
-  //       `
-  //       <script setup>
-  //       const count = ref(0)
-  //       defineExpose({ count })
-  //       </script>
-  //       `,
-  //       { inlineTemplate: true }
-  //     )
-  //     assertCode(content)
-  //     expect(content).toMatch(`setup(__props, { expose })`)
-  //     expect(content).toMatch(`expose({ count })`)
-  //   })
-
-  //   test('referencing scope components and directives', () => {
-  //     const { content } = compile(
-  //       `
-  //       <script setup>
-  //       import ChildComp from './Child.vue'
-  //       import SomeOtherComp from './Other.vue'
-  //       import vMyDir from './my-dir'
-  //       </script>
-  //       <template>
-  //         <div v-my-dir></div>
-  //         <ChildComp/>
-  //         <some-other-comp/>
-  //       </template>
-  //       `,
-  //       { inlineTemplate: true }
-  //     )
-  //     expect(content).toMatch('[_unref(vMyDir)]')
-  //     expect(content).toMatch('_createVNode(ChildComp)')
-  //     // kebab-case component support
-  //     expect(content).toMatch('_createVNode(SomeOtherComp)')
-  //     assertCode(content)
-  //   })
-
-  //   test('avoid unref() when necessary', () => {
-  //     // function, const, component import
-  //     const { content } = compile(
-  //       `<script setup>
-  //       import { ref } from 'vue'
-  //       import Foo, { bar } from './Foo.vue'
-  //       import other from './util'
-  //       import * as tree from './tree'
-  //       const count = ref(0)
-  //       const constant = {}
-  //       const maybe = foo()
-  //       let lett = 1
-  //       function fn() {}
-  //       </script>
-  //       <template>
-  //         <Foo>{{ bar }}</Foo>
-  //         <div @click="fn">{{ count }} {{ constant }} {{ maybe }} {{ lett }} {{ other }}</div>
-  //         {{ tree.foo() }}
-  //       </template>
-  //       `,
-  //       { inlineTemplate: true }
-  //     )
-  //     // no need to unref vue component import
-  //     expect(content).toMatch(`createVNode(Foo,`)
-  //     // #2699 should unref named imports from .vue
-  //     expect(content).toMatch(`unref(bar)`)
-  //     // should unref other imports
-  //     expect(content).toMatch(`unref(other)`)
-  //     // no need to unref constant literals
-  //     expect(content).not.toMatch(`unref(constant)`)
-  //     // should directly use .value for known refs
-  //     expect(content).toMatch(`count.value`)
-  //     // should unref() on const bindings that may be refs
-  //     expect(content).toMatch(`unref(maybe)`)
-  //     // should unref() on let bindings
-  //     expect(content).toMatch(`unref(lett)`)
-  //     // no need to unref namespace import (this also preserves tree-shaking)
-  //     expect(content).toMatch(`tree.foo()`)
-  //     // no need to unref function declarations
-  //     expect(content).toMatch(`{ onClick: fn }`)
-  //     // no need to mark constant fns in patch flag
-  //     expect(content).not.toMatch(`PROPS`)
-  //     assertCode(content)
-  //   })
-
-  //   test('v-model codegen', () => {
-  //     const { content } = compile(
-  //       `<script setup>
-  //       import { ref } from 'vue'
-  //       const count = ref(0)
-  //       const maybe = foo()
-  //       let lett = 1
-  //       </script>
-  //       <template>
-  //         <input v-model="count">
-  //         <input v-model="maybe">
-  //         <input v-model="lett">
-  //       </template>
-  //       `,
-  //       { inlineTemplate: true }
-  //     )
-  //     // known const ref: set value
-  //     expect(content).toMatch(`(count).value = $event`)
-  //     // const but maybe ref: assign if ref, otherwise do nothing
-  //     expect(content).toMatch(`_isRef(maybe) ? (maybe).value = $event : null`)
-  //     // let: handle both cases
-  //     expect(content).toMatch(
-  //       `_isRef(lett) ? (lett).value = $event : lett = $event`
-  //     )
-  //     assertCode(content)
-  //   })
-
-  //   test('template assignment expression codegen', () => {
-  //     const { content } = compile(
-  //       `<script setup>
-  //       import { ref } from 'vue'
-  //       const count = ref(0)
-  //       const maybe = foo()
-  //       let lett = 1
-  //       let v = ref(1)
-  //       </script>
-  //       <template>
-  //         <div @click="count = 1"/>
-  //         <div @click="maybe = count"/>
-  //         <div @click="lett = count"/>
-  //         <div @click="v += 1"/>
-  //         <div @click="v -= 1"/>
-  //         <div @click="() => {
-  //             let a = '' + lett
-  //             v = a
-  //          }"/>
-  //          <div @click="() => {
-  //             // nested scopes
-  //             (()=>{
-  //               let x = a
-  //               (()=>{
-  //                 let z = x
-  //                 let z2 = z
-  //               })
-  //               let lz = z
-  //             })
-  //             v = a
-  //          }"/>
-  //       </template>
-  //       `,
-  //       { inlineTemplate: true }
-  //     )
-  //     // known const ref: set value
-  //     expect(content).toMatch(`count.value = 1`)
-  //     // const but maybe ref: only assign after check
-  //     expect(content).toMatch(`maybe.value = count.value`)
-  //     // let: handle both cases
-  //     expect(content).toMatch(
-  //       `_isRef(lett) ? lett.value = count.value : lett = count.value`
-  //     )
-  //     expect(content).toMatch(`_isRef(v) ? v.value += 1 : v += 1`)
-  //     expect(content).toMatch(`_isRef(v) ? v.value -= 1 : v -= 1`)
-  //     expect(content).toMatch(`_isRef(v) ? v.value = a : v = a`)
-  //     expect(content).toMatch(`_isRef(v) ? v.value = _ctx.a : v = _ctx.a`)
-  //     assertCode(content)
-  //   })
-
-  //   test('template update expression codegen', () => {
-  //     const { content } = compile(
-  //       `<script setup>
-  //       import { ref } from 'vue'
-  //       const count = ref(0)
-  //       const maybe = foo()
-  //       let lett = 1
-  //       </script>
-  //       <template>
-  //         <div @click="count++"/>
-  //         <div @click="--count"/>
-  //         <div @click="maybe++"/>
-  //         <div @click="--maybe"/>
-  //         <div @click="lett++"/>
-  //         <div @click="--lett"/>
-  //       </template>
-  //       `,
-  //       { inlineTemplate: true }
-  //     )
-  //     // known const ref: set value
-  //     expect(content).toMatch(`count.value++`)
-  //     expect(content).toMatch(`--count.value`)
-  //     // const but maybe ref (non-ref case ignored)
-  //     expect(content).toMatch(`maybe.value++`)
-  //     expect(content).toMatch(`--maybe.value`)
-  //     // let: handle both cases
-  //     expect(content).toMatch(`_isRef(lett) ? lett.value++ : lett++`)
-  //     expect(content).toMatch(`_isRef(lett) ? --lett.value : --lett`)
-  //     assertCode(content)
-  //   })
-
-  //   test('template destructure assignment codegen', () => {
-  //     const { content } = compile(
-  //       `<script setup>
-  //       import { ref } from 'vue'
-  //       const val = {}
-  //       const count = ref(0)
-  //       const maybe = foo()
-  //       let lett = 1
-  //       </script>
-  //       <template>
-  //         <div @click="({ count } = val)"/>
-  //         <div @click="[maybe] = val"/>
-  //         <div @click="({ lett } = val)"/>
-  //       </template>
-  //       `,
-  //       { inlineTemplate: true }
-  //     )
-  //     // known const ref: set value
-  //     expect(content).toMatch(`({ count: count.value } = val)`)
-  //     // const but maybe ref (non-ref case ignored)
-  //     expect(content).toMatch(`[maybe.value] = val`)
-  //     // let: assumes non-ref
-  //     expect(content).toMatch(`{ lett: lett } = val`)
-  //     assertCode(content)
-  //   })
-
-  //   test('ssr codegen', () => {
-  //     const { content } = compile(
-  //       `
-  //       <script setup>
-  //       import { ref } from 'vue'
-  //       const count = ref(0)
-  //       </script>
-  //       <template>
-  //         <div>{{ count }}</div>
-  //         <div>static</div>
-  //       </template>
-  //       <style>
-  //       div { color: v-bind(count) }
-  //       </style>
-  //       `,
-  //       {
-  //         inlineTemplate: true,
-  //         templateOptions: {
-  //           ssr: true
-  //         }
-  //       }
-  //     )
-  //     expect(content).toMatch(`\n  __ssrInlineRender: true,\n`)
-  //     expect(content).toMatch(`return (_ctx, _push`)
-  //     expect(content).toMatch(`ssrInterpolate`)
-  //     expect(content).not.toMatch(`useCssVars`)
-  //     expect(content).toMatch(`"--${mockId}-count": (count.value)`)
-  //     assertCode(content)
-  //   })
-  // })
-
-  describe('with TypeScript', () => {
-    test('hoist type declarations', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-        export interface Foo {}
-        type Bar = {}
-      </script>`)
-      assertCode(content)
-    })
-
-    test('defineProps/Emit w/ runtime options', () => {
-      const { content } = compile(`
-<script setup lang="ts">
-const props = defineProps({ foo: String })
-const emit = defineEmits(['a', 'b'])
-</script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`export default /*#__PURE__*/_defineComponent({
-  props: { foo: String },
-  emits: ['a', 'b'],
-  setup(__props, { emit }) {`)
-    })
-
-    test('defineProps w/ type', () => {
-      const { content, bindings } = compile(`
-      <script setup lang="ts">
-      interface Test {}
-
-      type Alias = number[]
-
-      defineProps<{
-        string: string
-        number: number
-        boolean: boolean
-        object: object
-        objectLiteral: { a: number }
-        fn: (n: number) => void
-        functionRef: Function
-        objectRef: Object
-        dateTime: Date
-        array: string[]
-        arrayRef: Array<any>
-        tuple: [number, number]
-        set: Set<string>
-        literal: 'foo'
-        optional?: any
-        recordRef: Record<string, null>
-        interface: Test
-        alias: Alias
-        method(): void
-        symbol: symbol
-
-        union: string | number
-        literalUnion: 'foo' | 'bar'
-        literalUnionNumber: 1 | 2 | 3 | 4 | 5
-        literalUnionMixed: 'foo' | 1 | boolean
-        intersection: Test & {}
-        foo: ((item: any) => boolean) | null
-      }>()
-      </script>`)
-      assertCode(content)
-      expect(content).toMatch(`string: { type: String, required: true }`)
-      expect(content).toMatch(`number: { type: Number, required: true }`)
-      expect(content).toMatch(`boolean: { type: Boolean, required: true }`)
-      expect(content).toMatch(`object: { type: Object, required: true }`)
-      expect(content).toMatch(`objectLiteral: { type: Object, required: true }`)
-      expect(content).toMatch(`fn: { type: Function, required: true }`)
-      expect(content).toMatch(`functionRef: { type: Function, required: true }`)
-      expect(content).toMatch(`objectRef: { type: Object, required: true }`)
-      expect(content).toMatch(`dateTime: { type: Date, required: true }`)
-      expect(content).toMatch(`array: { type: Array, required: true }`)
-      expect(content).toMatch(`arrayRef: { type: Array, required: true }`)
-      expect(content).toMatch(`tuple: { type: Array, required: true }`)
-      expect(content).toMatch(`set: { type: Set, required: true }`)
-      expect(content).toMatch(`literal: { type: String, required: true }`)
-      expect(content).toMatch(`optional: { type: null, required: false }`)
-      expect(content).toMatch(`recordRef: { type: Object, required: true }`)
-      expect(content).toMatch(`interface: { type: Object, required: true }`)
-      expect(content).toMatch(`alias: { type: Array, required: true }`)
-      expect(content).toMatch(`method: { type: Function, required: true }`)
-      expect(content).toMatch(`symbol: { type: Symbol, required: true }`)
-      expect(content).toMatch(
-        `union: { type: [String, Number], required: true }`
-      )
-      expect(content).toMatch(`literalUnion: { type: String, required: true }`)
-      expect(content).toMatch(
-        `literalUnionNumber: { type: Number, required: true }`
-      )
-      expect(content).toMatch(
-        `literalUnionMixed: { type: [String, Number, Boolean], required: true }`
-      )
-      expect(content).toMatch(`intersection: { type: Object, required: true }`)
-      expect(content).toMatch(`foo: { type: [Function, null], required: true }`)
-      expect(bindings).toStrictEqual({
-        string: BindingTypes.PROPS,
-        number: BindingTypes.PROPS,
-        boolean: BindingTypes.PROPS,
-        object: BindingTypes.PROPS,
-        objectLiteral: BindingTypes.PROPS,
-        fn: BindingTypes.PROPS,
-        functionRef: BindingTypes.PROPS,
-        objectRef: BindingTypes.PROPS,
-        dateTime: BindingTypes.PROPS,
-        array: BindingTypes.PROPS,
-        arrayRef: BindingTypes.PROPS,
-        tuple: BindingTypes.PROPS,
-        set: BindingTypes.PROPS,
-        literal: BindingTypes.PROPS,
-        optional: BindingTypes.PROPS,
-        recordRef: BindingTypes.PROPS,
-        interface: BindingTypes.PROPS,
-        alias: BindingTypes.PROPS,
-        method: BindingTypes.PROPS,
-        symbol: BindingTypes.PROPS,
-        union: BindingTypes.PROPS,
-        literalUnion: BindingTypes.PROPS,
-        literalUnionNumber: BindingTypes.PROPS,
-        literalUnionMixed: BindingTypes.PROPS,
-        intersection: BindingTypes.PROPS,
-        foo: BindingTypes.PROPS
-      })
-    })
-
-    test('defineProps w/ interface', () => {
-      const { content, bindings } = compile(`
-      <script setup lang="ts">
-      interface Props { x?: number }
-      defineProps<Props>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`x: { type: Number, required: false }`)
-      expect(bindings).toStrictEqual({
-        x: BindingTypes.PROPS
-      })
-    })
-
-    test('defineProps w/ exported interface', () => {
-      const { content, bindings } = compile(`
-      <script setup lang="ts">
-      export interface Props { x?: number }
-      defineProps<Props>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`x: { type: Number, required: false }`)
-      expect(bindings).toStrictEqual({
-        x: BindingTypes.PROPS
-      })
-    })
-
-    test('defineProps w/ exported interface in normal script', () => {
-      const { content, bindings } = compile(`
-      <script lang="ts">
-        export interface Props { x?: number }
-      </script>
-      <script setup lang="ts">
-        defineProps<Props>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`x: { type: Number, required: false }`)
-      expect(bindings).toStrictEqual({
-        x: BindingTypes.PROPS
-      })
-    })
-
-    test('defineProps w/ type alias', () => {
-      const { content, bindings } = compile(`
-      <script setup lang="ts">
-      type Props = { x?: number }
-      defineProps<Props>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`x: { type: Number, required: false }`)
-      expect(bindings).toStrictEqual({
-        x: BindingTypes.PROPS
-      })
-    })
-
-    test('defineProps w/ exported type alias', () => {
-      const { content, bindings } = compile(`
-      <script setup lang="ts">
-      export type Props = { x?: number }
-      defineProps<Props>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`x: { type: Number, required: false }`)
-      expect(bindings).toStrictEqual({
-        x: BindingTypes.PROPS
-      })
-    })
-
-    test('withDefaults (static)', () => {
-      const { content, bindings } = compile(`
-      <script setup lang="ts">
-      const props = withDefaults(defineProps<{
-        foo?: string
-        bar?: number;
-        baz: boolean;
-        qux?(): number
-      }>(), {
-        foo: 'hi',
-        qux() { return 1 }
-      })
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(
-        `foo: { type: String, required: false, default: 'hi' }`
-      )
-      expect(content).toMatch(`bar: { type: Number, required: false }`)
-      expect(content).toMatch(`baz: { type: Boolean, required: true }`)
-      expect(content).toMatch(
-        `qux: { type: Function, required: false, default() { return 1 } }`
-      )
-      expect(content).toMatch(
-        `{ foo: string, bar?: number, baz: boolean, qux(): number }`
-      )
-      expect(content).toMatch(`const props = __props`)
-      expect(bindings).toStrictEqual({
-        foo: BindingTypes.PROPS,
-        bar: BindingTypes.PROPS,
-        baz: BindingTypes.PROPS,
-        qux: BindingTypes.PROPS,
-        props: BindingTypes.SETUP_CONST
-      })
-    })
-
-    test('withDefaults (dynamic)', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      import { defaults } from './foo'
-      const props = withDefaults(defineProps<{
-        foo?: string
-        bar?: number
-        baz: boolean
-      }>(), { ...defaults })
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`import { mergeDefaults as _mergeDefaults`)
-      expect(content).toMatch(
-        `
-  _mergeDefaults({
-    foo: { type: String, required: false },
-    bar: { type: Number, required: false },
-    baz: { type: Boolean, required: true }
-  }, { ...defaults })`.trim()
-      )
-    })
-
-    test('defineEmits w/ type', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      const emit = defineEmits<(e: 'foo' | 'bar') => void>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
-      expect(content).toMatch(`emits: ["foo", "bar"]`)
-    })
-
-    test('defineEmits w/ type (union)', () => {
-      const type = `((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)`
-      expect(() =>
-        compile(`
-      <script setup lang="ts">
-      const emit = defineEmits<${type}>()
-      </script>
-      `)
-      ).toThrow()
-    })
-
-    test('defineEmits w/ type (type literal w/ call signatures)', () => {
-      const type = `{(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}`
-      const { content } = compile(`
-      <script setup lang="ts">
-      const emit = defineEmits<${type}>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`emit: (${type}),`)
-      expect(content).toMatch(`emits: ["foo", "bar", "baz"]`)
-    })
-
-    test('defineEmits w/ type (interface)', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      interface Emits { (e: 'foo' | 'bar'): void }
-      const emit = defineEmits<Emits>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
-      expect(content).toMatch(`emits: ["foo", "bar"]`)
-    })
-
-    test('defineEmits w/ type (exported interface)', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      export interface Emits { (e: 'foo' | 'bar'): void }
-      const emit = defineEmits<Emits>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
-      expect(content).toMatch(`emits: ["foo", "bar"]`)
-    })
-
-    test('defineEmits w/ type (type alias)', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      type Emits = { (e: 'foo' | 'bar'): void }
-      const emit = defineEmits<Emits>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
-      expect(content).toMatch(`emits: ["foo", "bar"]`)
-    })
-
-    test('defineEmits w/ type (exported type alias)', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      export type Emits = { (e: 'foo' | 'bar'): void }
-      const emit = defineEmits<Emits>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
-      expect(content).toMatch(`emits: ["foo", "bar"]`)
-    })
-
-    test('defineEmits w/ type (referenced function type)', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      type Emits = (e: 'foo' | 'bar') => void
-      const emit = defineEmits<Emits>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
-      expect(content).toMatch(`emits: ["foo", "bar"]`)
-    })
-
-    test('defineEmits w/ type (referenced exported function type)', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      export type Emits = (e: 'foo' | 'bar') => void
-      const emit = defineEmits<Emits>()
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
-      expect(content).toMatch(`emits: ["foo", "bar"]`)
-    })
-
-    // https://github.com/vuejs/core/issues/5393
-    test('defineEmits w/ type (interface ts type)', () => {
-      const { content } = compile(`
-      <script setup lang="ts">
-      interface Emits { (e: 'foo'): void }
-      const emit: Emits = defineEmits(['foo'])
-      </script>
-      `)
-      assertCode(content)
-      expect(content).toMatch(`setup(__props, { emit }) {`)
-      expect(content).toMatch(`emits: ['foo']`)
-    })
-
-    test('runtime Enum', () => {
-      const { content, bindings } = compile(
-        `<script setup lang="ts">
-        enum Foo { A = 123 }
-        </script>`
-      )
-      assertCode(content)
-      expect(bindings).toStrictEqual({
-        Foo: BindingTypes.SETUP_CONST
-      })
-    })
-
-    test('runtime Enum in normal script', () => {
-      const { content, bindings } = compile(
-        `<script lang="ts">
-          export enum D { D = "D" }
-          const enum C { C = "C" }
-          enum B { B = "B" }
-        </script>
-        <script setup lang="ts">
-        enum Foo { A = 123 }
-        </script>`
-      )
-      assertCode(content)
-      expect(bindings).toStrictEqual({
-        D: BindingTypes.SETUP_CONST,
-        C: BindingTypes.SETUP_CONST,
-        B: BindingTypes.SETUP_CONST,
-        Foo: BindingTypes.SETUP_CONST
-      })
-    })
-
-    test('const Enum', () => {
-      const { content, bindings } = compile(
-        `<script setup lang="ts">
-        const enum Foo { A = 123 }
-        </script>`
-      )
-      assertCode(content)
-      expect(bindings).toStrictEqual({
-        Foo: BindingTypes.SETUP_CONST
-      })
-    })
-
-    test('import type', () => {
-      const { content } = compile(
-        `<script setup lang="ts">
-        import type { Foo } from './main.ts'
-        import { type Bar, Baz } from './main.ts'
-        </script>`
-      )
-      expect(content).toMatch(`return { Baz }`)
-      assertCode(content)
-    })
-  })
-
-  describe('errors', () => {
-    test('<script> and <script setup> must have same lang', () => {
-      expect(() =>
-        compile(`<script>foo()</script><script setup lang="ts">bar()</script>`)
-      ).toThrow(`<script> and <script setup> must have the same language type`)
-    })
-
-    const moduleErrorMsg = `cannot contain ES module exports`
-
-    test('non-type named exports', () => {
-      expect(() =>
-        compile(`<script setup>
-        export const a = 1
-        </script>`)
-      ).toThrow(moduleErrorMsg)
-
-      expect(() =>
-        compile(`<script setup>
-        export * from './foo'
-        </script>`)
-      ).toThrow(moduleErrorMsg)
-
-      expect(() =>
-        compile(`<script setup>
-          const bar = 1
-          export { bar as default }
-        </script>`)
-      ).toThrow(moduleErrorMsg)
-    })
-
-    test('defineProps/Emit() w/ both type and non-type args', () => {
-      expect(() => {
-        compile(`<script setup lang="ts">
-        defineProps<{}>({})
-        </script>`)
-      }).toThrow(`cannot accept both type and non-type arguments`)
-
-      expect(() => {
-        compile(`<script setup lang="ts">
-        defineEmits<{}>({})
-        </script>`)
-      }).toThrow(`cannot accept both type and non-type arguments`)
-    })
-
-    test('defineProps/Emit() referencing local var', () => {
-      expect(() =>
-        compile(`<script setup>
-        const bar = 1
-        defineProps({
-          foo: {
-            default: () => bar
-          }
-        })
-        </script>`)
-      ).toThrow(`cannot reference locally declared variables`)
-
-      expect(() =>
-        compile(`<script setup>
-        const bar = 'hello'
-        defineEmits([bar])
-        </script>`)
-      ).toThrow(`cannot reference locally declared variables`)
-
-      // #4644
-      expect(() =>
-        compile(`
-        <script>const bar = 1</script>
-        <script setup>
-        defineProps({
-          foo: {
-            default: () => bar
-          }
-        })
-        </script>`)
-      ).not.toThrow(`cannot reference locally declared variables`)
-    })
-
-    test('should allow defineProps/Emit() referencing scope var', () => {
-      assertCode(
-        compile(`<script setup>
-          const bar = 1
-          defineProps({
-            foo: {
-              default: bar => bar + 1
-            }
-          })
-          defineEmits({
-            foo: bar => bar > 1
-          })
-        </script>`).content
-      )
-    })
-
-    test('should allow defineProps/Emit() referencing imported binding', () => {
-      assertCode(
-        compile(`<script setup>
-        import { bar } from './bar'
-        defineProps({
-          foo: {
-            default: () => bar
-          }
-        })
-        defineEmits({
-          foo: () => bar > 1
-        })
-        </script>`).content
-      )
-    })
-  })
-})
-
-describe('SFC analyze <script> bindings', () => {
-  it('can parse decorators syntax in typescript block', () => {
-    const { scriptAst } = compile(`
-      <script lang="ts">
-        import { Options, Vue } from 'vue-class-component';
-        @Options({
-          components: {
-            HelloWorld,
-          },
-          props: ['foo', 'bar']
-        })
-        export default class Home extends Vue {}
-      </script>
-    `)
-
-    expect(scriptAst).toBeDefined()
-  })
-
-  it('recognizes props array declaration', () => {
-    const { bindings } = compile(`
-      <script>
-        export default {
-          props: ['foo', 'bar']
-        }
-      </script>
-    `)
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.PROPS,
-      bar: BindingTypes.PROPS
-    })
-    expect(bindings!.__isScriptSetup).toBe(false)
-  })
-
-  it('recognizes props object declaration', () => {
-    const { bindings } = compile(`
-      <script>
-        export default {
-          props: {
-            foo: String,
-            bar: {
-              type: String,
-            },
-            baz: null,
-            qux: [String, Number]
-          }
-        }
-      </script>
-    `)
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.PROPS,
-      bar: BindingTypes.PROPS,
-      baz: BindingTypes.PROPS,
-      qux: BindingTypes.PROPS
-    })
-    expect(bindings!.__isScriptSetup).toBe(false)
-  })
-
-  it('recognizes setup return', () => {
-    const { bindings } = compile(`
-      <script>
-        const bar = 2
-        export default {
-          setup() {
-            return {
-              foo: 1,
-              bar
-            }
-          }
-        }
-      </script>
-    `)
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.SETUP_MAYBE_REF,
-      bar: BindingTypes.SETUP_MAYBE_REF
-    })
-    expect(bindings!.__isScriptSetup).toBe(false)
-  })
-
-  it('recognizes exported vars', () => {
-    const { bindings } = compile(`
-      <script>
-        export const foo = 2
-      </script>
-      <script setup>
-        console.log(foo)
-      </script>
-    `)
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.SETUP_CONST
-    })
-  })
-
-  it('recognizes async setup return', () => {
-    const { bindings } = compile(`
-      <script>
-        const bar = 2
-        export default {
-          async setup() {
-            return {
-              foo: 1,
-              bar
-            }
-          }
-        }
-      </script>
-    `)
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.SETUP_MAYBE_REF,
-      bar: BindingTypes.SETUP_MAYBE_REF
-    })
-    expect(bindings!.__isScriptSetup).toBe(false)
-  })
-
-  it('recognizes data return', () => {
-    const { bindings } = compile(`
-      <script>
-        const bar = 2
-        export default {
-          data() {
-            return {
-              foo: null,
-              bar
-            }
-          }
-        }
-      </script>
-    `)
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.DATA,
-      bar: BindingTypes.DATA
-    })
-  })
-
-  it('recognizes methods', () => {
-    const { bindings } = compile(`
-      <script>
-        export default {
-          methods: {
-            foo() {}
-          }
-        }
-      </script>
-    `)
-    expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS })
-  })
-
-  it('recognizes computeds', () => {
-    const { bindings } = compile(`
-      <script>
-        export default {
-          computed: {
-            foo() {},
-            bar: {
-              get() {},
-              set() {},
-            }
-          }
-        }
-      </script>
-    `)
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.OPTIONS,
-      bar: BindingTypes.OPTIONS
-    })
-  })
-
-  it('recognizes injections array declaration', () => {
-    const { bindings } = compile(`
-      <script>
-        export default {
-          inject: ['foo', 'bar']
-        }
-      </script>
-    `)
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.OPTIONS,
-      bar: BindingTypes.OPTIONS
-    })
-  })
-
-  it('recognizes injections object declaration', () => {
-    const { bindings } = compile(`
-      <script>
-        export default {
-          inject: {
-            foo: {},
-            bar: {},
-          }
-        }
-      </script>
-    `)
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.OPTIONS,
-      bar: BindingTypes.OPTIONS
-    })
-  })
-
-  it('works for mixed bindings', () => {
-    const { bindings } = compile(`
-      <script>
-        export default {
-          inject: ['foo'],
-          props: {
-            bar: String,
-          },
-          setup() {
-            return {
-              baz: null,
-            }
-          },
-          data() {
-            return {
-              qux: null
-            }
-          },
-          methods: {
-            quux() {}
-          },
-          computed: {
-            quuz() {}
-          }
-        }
-      </script>
-    `)
-    expect(bindings).toStrictEqual({
-      foo: BindingTypes.OPTIONS,
-      bar: BindingTypes.PROPS,
-      baz: BindingTypes.SETUP_MAYBE_REF,
-      qux: BindingTypes.DATA,
-      quux: BindingTypes.OPTIONS,
-      quuz: BindingTypes.OPTIONS
-    })
-  })
-
-  it('works for script setup', () => {
-    const { bindings } = compile(`
-      <script setup>
-      import { ref as r } from 'vue'
-      defineProps({
-        foo: String
-      })
-
-      const a = r(1)
-      let b = 2
-      const c = 3
-      const { d } = someFoo()
-      let { e } = someBar()
-      </script>
-    `)
-
-    expect(bindings).toStrictEqual({
-      r: BindingTypes.SETUP_CONST,
-      a: BindingTypes.SETUP_REF,
-      b: BindingTypes.SETUP_LET,
-      c: BindingTypes.SETUP_CONST,
-      d: BindingTypes.SETUP_MAYBE_REF,
-      e: BindingTypes.SETUP_LET,
-      foo: BindingTypes.PROPS
-    })
-  })
-
-  describe('auto name inference', () => {
-    test('basic', () => {
-      const { content } = compile(
-        `<script setup>const a = 1</script>
-        <template>{{ a }}</template>`,
-        undefined,
-        {
-          filename: 'FooBar.vue'
-        }
-      )
-      expect(content).toMatch(`export default {
-  __name: 'FooBar'`)
-      assertCode(content)
-    })
-
-    test('do not overwrite manual name (object)', () => {
-      const { content } = compile(
-        `<script>
-        export default {
-          name: 'Baz'
-        }
-        </script>
-        <script setup>const a = 1</script>
-        <template>{{ a }}</template>`,
-        undefined,
-        {
-          filename: 'FooBar.vue'
-        }
-      )
-      expect(content).not.toMatch(`name: 'FooBar'`)
-      expect(content).toMatch(`name: 'Baz'`)
-      assertCode(content)
-    })
-
-    test('do not overwrite manual name (call)', () => {
-      const { content } = compile(
-        `<script>
-        import { defineComponent } from 'vue'
-        export default defineComponent({
-          name: 'Baz'
-        })
-        </script>
-        <script setup>const a = 1</script>
-        <template>{{ a }}</template>`,
-        undefined,
-        {
-          filename: 'FooBar.vue'
-        }
-      )
-      expect(content).not.toMatch(`name: 'FooBar'`)
-      expect(content).toMatch(`name: 'Baz'`)
-      assertCode(content)
-    })
-
-    // #12591
-    test('should not error when performing ts expression check for v-on inline statement', () => {
-      compile(`
-      <script setup lang="ts">
-        import { foo } from './foo'
-      </script>
-      <template>
-        <div @click="$emit('update:a');"></div>
-      </template>
-      `)
-    })
-
-    // #12841
-    test('should not error when performing ts expression check for v-slot destructured default value', () => {
-      compile(`
-      <script setup lang="ts">
-        import FooComp from './Foo.vue'
-      </script>
-      <template>
-        <FooComp>
-          <template #bar="{ bar = { baz: '' } }">
-            {{ bar.baz }}
-          </template>
-        </FooComp>
-      </template>
-      `)
-    })
-  })
-})
diff --git a/packages/compiler-sfc/test/compileStyle.spec.ts b/packages/compiler-sfc/test/compileStyle.spec.ts
deleted file mode 100644
index 8ad69ef42ff..00000000000
--- a/packages/compiler-sfc/test/compileStyle.spec.ts
+++ /dev/null
@@ -1,203 +0,0 @@
-import { parse } from '../src/parse'
-import { compileStyle, compileStyleAsync } from '../src/compileStyle'
-
-test('preprocess less', () => {
-  const style = parse({
-    source:
-      '<style lang="less">\n' +
-      '@red: rgb(255, 0, 0);\n' +
-      '.color { color: @red; }\n' +
-      '</style>\n',
-    filename: 'example.vue',
-    sourceMap: true
-  }).styles[0]
-
-  const result = compileStyle({
-    id: 'v-scope-xxx',
-    filename: 'example.vue',
-    source: style.content,
-    map: style.map,
-    scoped: false,
-    preprocessLang: style.lang
-  })
-
-  expect(result.errors.length).toBe(0)
-  expect(result.code).toEqual(expect.stringContaining('color: #ff0000;'))
-  expect(result.map).toBeTruthy()
-})
-
-test('preprocess scss', () => {
-  const style = parse({
-    source:
-      '<style lang="scss">\n' +
-      '$red: red;\n' +
-      '.color { color: $red; }\n' +
-      '</style>\n',
-    filename: 'example.vue',
-    sourceMap: true
-  }).styles[0]
-  const result = compileStyle({
-    id: 'v-scope-xxx',
-    filename: 'example.vue',
-    source: style.content,
-    map: style.map,
-    scoped: false,
-    preprocessLang: style.lang
-  })
-
-  expect(result.errors.length).toBe(0)
-  expect(result.code).toMatch('color: red;')
-  expect(result.map).toBeTruthy()
-})
-
-test('preprocess sass', () => {
-  const style = parse({
-    source:
-      '<style lang="sass">\n' +
-      '$red: red\n' +
-      '.color\n' +
-      '   color: $red\n' +
-      '</style>\n',
-    filename: 'example.vue',
-    sourceMap: true
-  }).styles[0]
-  const result = compileStyle({
-    id: 'v-scope-xxx',
-    filename: 'example.vue',
-    source: style.content,
-    map: style.map,
-    scoped: false,
-    preprocessLang: style.lang
-  })
-
-  expect(result.errors.length).toBe(0)
-  expect(result.code).toMatch('color: red;')
-  expect(result.map).toBeTruthy()
-})
-
-test('preprocess stylus', () => {
-  const style = parse({
-    source:
-      '<style lang="styl">\n' +
-      'red-color = rgb(255, 0, 0);\n' +
-      '.color\n' +
-      '   color: red-color\n' +
-      '</style>\n',
-    filename: 'example.vue',
-    sourceMap: true
-  }).styles[0]
-  const result = compileStyle({
-    id: 'v-scope-xxx',
-    filename: 'example.vue',
-    source: style.content,
-    map: style.map,
-    scoped: false,
-    preprocessLang: style.lang
-  })
-
-  expect(result.errors.length).toBe(0)
-  expect(result.code).toEqual(expect.stringContaining('color: #f00;'))
-  expect(result.map).toBeTruthy()
-})
-
-test('custom postcss plugin', () => {
-  const spy = vi.fn()
-
-  compileStyle({
-    id: 'v-scope-xxx',
-    filename: 'example.vue',
-    source: '.foo { color: red }',
-    scoped: false,
-    postcssPlugins: [require('postcss').plugin('test-plugin', () => spy)()]
-  })
-
-  expect(spy).toHaveBeenCalled()
-})
-
-test('custom postcss options', () => {
-  const result = compileStyle({
-    id: 'v-scope-xxx',
-    filename: 'example.vue',
-    source: '.foo { color: red }',
-    scoped: false,
-    postcssOptions: { random: 'foo' }
-  })
-
-  expect((result.rawResult as any).opts.random).toBe('foo')
-})
-
-test('async postcss plugin in sync mode', () => {
-  const result = compileStyle({
-    id: 'v-scope-xxx',
-    filename: 'example.vue',
-    source: '.foo { color: red }',
-    scoped: false,
-    postcssPlugins: [
-      require('postcss').plugin(
-        'test-plugin',
-        () => async (result: any) => result
-      )
-    ]
-  })
-
-  expect(result.errors).toHaveLength(1)
-})
-
-test('async postcss plugin', async () => {
-  const promise = compileStyleAsync({
-    id: 'v-scope-xxx',
-    filename: 'example.vue',
-    source: '.foo { color: red }',
-    scoped: false,
-    postcssPlugins: [
-      require('postcss').plugin(
-        'test-plugin',
-        () => async (result: any) => result
-      )
-    ]
-  })
-
-  expect(promise instanceof Promise).toBe(true)
-
-  const result = await promise
-  expect(result.errors).toHaveLength(0)
-  expect(result.code).toEqual(expect.stringContaining('color: red'))
-})
-
-test('media query', () => {
-  const result = compileStyle({
-    id: 'v-scope-xxx',
-    scoped: true,
-    filename: 'example.vue',
-    source: `
-@media print {
-  .foo {
-    color: #000;
-  }
-}`
-  })
-
-  expect(result.errors).toHaveLength(0)
-  expect(result.code).toContain(
-    '@media print {\n.foo[v-scope-xxx] {\n    color: #000;\n}\n}'
-  )
-})
-
-test('supports query', () => {
-  const result = compileStyle({
-    id: 'v-scope-xxx',
-    scoped: true,
-    filename: 'example.vue',
-    source: `
-@supports ( color: #000 ) {
-  .foo {
-    color: #000;
-  }
-}`
-  })
-
-  expect(result.errors).toHaveLength(0)
-  expect(result.code).toContain(
-    '@supports ( color: #000 ) {\n.foo[v-scope-xxx] {\n    color: #000;\n}\n}'
-  )
-})
diff --git a/packages/compiler-sfc/test/compileTemplate.spec.ts b/packages/compiler-sfc/test/compileTemplate.spec.ts
deleted file mode 100644
index a405d644a47..00000000000
--- a/packages/compiler-sfc/test/compileTemplate.spec.ts
+++ /dev/null
@@ -1,258 +0,0 @@
-import { parse } from '../src/parse'
-import { SFCBlock } from '../src/parseComponent'
-import { compileTemplate } from '../src/compileTemplate'
-import Vue from 'vue'
-
-function mockRender(code: string, mocks: Record<string, any> = {}) {
-  const fn = new Function(
-    `require`,
-    `${code}; return { render, staticRenderFns }`
-  )
-  const vm = new Vue(
-    Object.assign(
-      {},
-      fn((id: string) => mocks[id])
-    )
-  )
-  vm.$mount()
-  return (vm as any)._vnode
-}
-
-test('should work', () => {
-  const source = `<div><p>{{ render }}</p></div>`
-
-  const result = compileTemplate({
-    filename: 'example.vue',
-    source
-  })
-
-  expect(result.errors.length).toBe(0)
-  expect(result.source).toBe(source)
-  // should expose render fns
-  expect(result.code).toMatch(`var render = function`)
-  expect(result.code).toMatch(`var staticRenderFns = []`)
-  // should mark with stripped
-  expect(result.code).toMatch(`render._withStripped = true`)
-  // should prefix bindings
-  expect(result.code).toMatch(`_vm.render`)
-  expect(result.ast).not.toBeUndefined()
-})
-
-test('preprocess pug', () => {
-  const template = parse({
-    source:
-      '<template lang="pug">\n' +
-      'body\n' +
-      ' h1 Pug Examples\n' +
-      ' div.container\n' +
-      '   p Cool Pug example!\n' +
-      '</template>\n',
-    filename: 'example.vue',
-    sourceMap: true
-  }).template as SFCBlock
-
-  const result = compileTemplate({
-    filename: 'example.vue',
-    source: template.content,
-    preprocessLang: template.lang
-  })
-
-  expect(result.errors.length).toBe(0)
-})
-
-/**
- * vuejs/component-compiler-utils#22 Support uri fragment in transformed require
- */
-test('supports uri fragment in transformed require', () => {
-  const source = '<svg>\
-    <use href="~@svg/file.svg#fragment"></use>\
-  </svg>' //
-  const result = compileTemplate({
-    filename: 'svgparticle.html',
-    source: source,
-    transformAssetUrls: {
-      use: 'href'
-    }
-  })
-  expect(result.errors.length).toBe(0)
-  expect(result.code).toMatch(
-    /href: require\("@svg\/file.svg"\) \+ "#fragment"/
-  )
-})
-
-/**
- * vuejs/component-compiler-utils#22 Support uri fragment in transformed require
- */
-test('when too short uri then empty require', () => {
-  const source = '<svg>\
-    <use href="~"></use>\
-  </svg>' //
-  const result = compileTemplate({
-    filename: 'svgparticle.html',
-    source: source,
-    transformAssetUrls: {
-      use: 'href'
-    }
-  })
-  expect(result.errors.length).toBe(0)
-  expect(result.code).toMatch(/href: require\(""\)/)
-})
-
-test('warn missing preprocessor', () => {
-  const template = parse({
-    source: '<template lang="unknownLang">\n' + '</template>\n',
-
-    filename: 'example.vue',
-    sourceMap: true
-  }).template as SFCBlock
-
-  const result = compileTemplate({
-    filename: 'example.vue',
-    source: template.content,
-    preprocessLang: template.lang
-  })
-
-  expect(result.errors.length).toBe(1)
-})
-
-test('transform assetUrls', () => {
-  const source = `
-<div>
-  <img src="./logo.png">
-  <img src="~fixtures/logo.png">
-  <img src="~/fixtures/logo.png">
-</div>
-`
-  const result = compileTemplate({
-    filename: 'example.vue',
-    source,
-    transformAssetUrls: true
-  })
-  expect(result.errors.length).toBe(0)
-
-  const vnode = mockRender(result.code, {
-    './logo.png': 'a',
-    'fixtures/logo.png': 'b'
-  })
-
-  expect(vnode.children[0].data.attrs.src).toBe('a')
-  expect(vnode.children[2].data.attrs.src).toBe('b')
-  expect(vnode.children[4].data.attrs.src).toBe('b')
-})
-
-test('transform srcset', () => {
-  const source = `
-<div>
-  <img src="./logo.png">
-  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
-    <image xlink:href="./logo.png" />
-  </svg>
-  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
-    <use xlink:href="./logo.png"/>
-  </svg>
-  </svg>
-  <img src="./logo.png" srcset="./logo.png">
-  <img src="./logo.png" srcset="./logo.png 2x">
-  <img src="./logo.png" srcset="./logo.png, ./logo.png 2x">
-  <img src="./logo.png" srcset="./logo.png 2x, ./logo.png">
-  <img src="./logo.png" srcset="./logo.png 2x, ./logo.png 3x">
-  <img src="./logo.png" srcset="./logo.png, ./logo.png 2x, ./logo.png 3x">
-  <img
-    src="./logo.png"
-    srcset="
-      ./logo.png 2x,
-      ./logo.png 3x
-  ">
-</div>
-`
-  const result = compileTemplate({
-    filename: 'example.vue',
-    source,
-    transformAssetUrls: true
-  })
-  expect(result.errors.length).toBe(0)
-
-  const vnode = mockRender(result.code, {
-    './logo.png': 'test-url'
-  })
-
-  // img tag
-  expect(vnode.children[0].data.attrs.src).toBe('test-url')
-  // image tag (SVG)
-  expect(vnode.children[2].children[0].data.attrs['xlink:href']).toBe(
-    'test-url'
-  )
-  // use tag (SVG)
-  expect(vnode.children[4].children[0].data.attrs['xlink:href']).toBe(
-    'test-url'
-  )
-
-  // image tag with srcset
-  expect(vnode.children[6].data.attrs.srcset).toBe('test-url')
-  expect(vnode.children[8].data.attrs.srcset).toBe('test-url 2x')
-  // image tag with multiline srcset
-  expect(vnode.children[10].data.attrs.srcset).toBe('test-url, test-url 2x')
-  expect(vnode.children[12].data.attrs.srcset).toBe('test-url 2x, test-url')
-  expect(vnode.children[14].data.attrs.srcset).toBe('test-url 2x, test-url 3x')
-  expect(vnode.children[16].data.attrs.srcset).toBe(
-    'test-url, test-url 2x, test-url 3x'
-  )
-  expect(vnode.children[18].data.attrs.srcset).toBe('test-url 2x, test-url 3x')
-})
-
-test('transform assetUrls and srcset with base option', () => {
-  const source = `
-<div>
-  <img src="./logo.png">
-  <img src="~fixtures/logo.png">
-  <img src="~/fixtures/logo.png">
-  <img src="./logo.png" srcset="./logo.png 2x, ./logo.png 3x">
-  <img src="@/fixtures/logo.png">
-</div>
-`
-  const result = compileTemplate({
-    filename: 'example.vue',
-    source,
-    transformAssetUrls: true,
-    transformAssetUrlsOptions: { base: '/base/' }
-  })
-
-  expect(result.errors.length).toBe(0)
-
-  const vnode = mockRender(result.code, {
-    '@/fixtures/logo.png': 'aliased'
-  })
-  expect(vnode.children[0].data.attrs.src).toBe('/base/logo.png')
-  expect(vnode.children[2].data.attrs.src).toBe('/base/fixtures/logo.png')
-  expect(vnode.children[4].data.attrs.src).toBe('/base/fixtures/logo.png')
-  expect(vnode.children[6].data.attrs.srcset).toBe(
-    '/base/logo.png 2x, /base/logo.png 3x'
-  )
-  expect(vnode.children[8].data.attrs.src).toBe('aliased')
-})
-
-test('transform with includeAbsolute', () => {
-  const source = `
-  <div>
-    <img src="./logo.png">
-    <img src="/logo.png">
-    <img src="https://foo.com/logo.png">
-  </div>
-  `
-  const result = compileTemplate({
-    filename: 'example.vue',
-    source,
-    transformAssetUrls: true,
-    transformAssetUrlsOptions: { includeAbsolute: true }
-  })
-
-  expect(result.errors.length).toBe(0)
-
-  const vnode = mockRender(result.code, {
-    './logo.png': 'relative',
-    '/logo.png': 'absolute'
-  })
-  expect(vnode.children[0].data.attrs.src).toBe('relative')
-  expect(vnode.children[2].data.attrs.src).toBe('absolute')
-  expect(vnode.children[4].data.attrs.src).toBe('https://foo.com/logo.png')
-})
diff --git a/packages/compiler-sfc/test/cssVars.spec.ts b/packages/compiler-sfc/test/cssVars.spec.ts
deleted file mode 100644
index 86f4e969c91..00000000000
--- a/packages/compiler-sfc/test/cssVars.spec.ts
+++ /dev/null
@@ -1,247 +0,0 @@
-import { compileStyle, parse } from '../src'
-import { mockId, compile, assertCode } from './util'
-
-describe('CSS vars injection', () => {
-  test('generating correct code for nested paths', () => {
-    const { content } = compile(
-      `<script>const a = 1</script>\n` +
-        `<style>div{
-          color: v-bind(color);
-          font-size: v-bind('font.size');
-        }</style>`
-    )
-    expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
-  "${mockId}-color": (_vm.color),
-  "${mockId}-font_size": (_vm.font.size)
-})`)
-    assertCode(content)
-  })
-
-  test('w/ normal <script> binding analysis', () => {
-    const { content } = compile(
-      `<script>
-      export default {
-        setup() {
-          return {
-            size: ref('100px')
-          }
-        }
-      }
-      </script>\n` +
-        `<style>
-          div {
-            font-size: v-bind(size);
-          }
-        </style>`
-    )
-    expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
-  "${mockId}-size": (_vm.size)
-})`)
-    expect(content).toMatch(`import { useCssVars as _useCssVars } from 'vue'`)
-    assertCode(content)
-  })
-
-  test('w/ <script setup> binding analysis', () => {
-    const { content } = compile(
-      `<script setup>
-        import { defineProps, ref } from 'vue'
-        const color = 'red'
-        const size = ref('10px')
-        defineProps({
-          foo: String
-        })
-        </script>\n` +
-        `<style>
-          div {
-            color: v-bind(color);
-            font-size: v-bind(size);
-            border: v-bind(foo);
-          }
-        </style>`
-    )
-    // should handle:
-    // 1. local const bindings
-    // 2. local potential ref bindings
-    // 3. props bindings (analyzed)
-    expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
-  "${mockId}-color": (_setup.color),
-  "${mockId}-size": (_setup.size),
-  "${mockId}-foo": (_vm.foo)
-})`)
-    expect(content).toMatch(`import { useCssVars as _useCssVars } from 'vue'`)
-    assertCode(content)
-  })
-
-  test('should rewrite CSS vars in compileStyle', () => {
-    const { code } = compileStyle({
-      source: `.foo {
-        color: v-bind(color);
-        font-size: v-bind('font.size');
-      }`,
-      filename: 'test.css',
-      id: 'data-v-test'
-    })
-    expect(code).toMatchInlineSnapshot(`
-      ".foo[data-v-test] {
-              color: var(--test-color);
-              font-size: var(--test-font_size);
-      }"
-    `)
-  })
-
-  test('prod mode', () => {
-    const { content } = compile(
-      `<script>const a = 1</script>\n` +
-        `<style>div{
-          color: v-bind(color);
-          font-size: v-bind('font.size');
-        }</style>`,
-      { isProd: true }
-    )
-    expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
-  "4003f1a6": (_vm.color),
-  "41b6490a": (_vm.font.size)
-}))}`)
-
-    const { code } = compileStyle({
-      source: `.foo {
-        color: v-bind(color);
-        font-size: v-bind('font.size');
-      }`,
-      filename: 'test.css',
-      id: mockId,
-      isProd: true
-    })
-    expect(code).toMatchInlineSnapshot(`
-      ".foo[xxxxxxxx] {
-              color: var(--4003f1a6);
-              font-size: var(--41b6490a);
-      }"
-    `)
-  })
-
-  describe('codegen', () => {
-    test('<script> w/ no default export', () => {
-      assertCode(
-        compile(
-          `<script>const a = 1</script>\n` +
-            `<style>div{ color: v-bind(color); }</style>`
-        ).content
-      )
-    })
-
-    test('<script> w/ default export', () => {
-      assertCode(
-        compile(
-          `<script>export default { setup() {} }</script>\n` +
-            `<style>div{ color: v-bind(color); }</style>`
-        ).content
-      )
-    })
-
-    test('<script> w/ default export in strings/comments', () => {
-      assertCode(
-        compile(
-          `<script>
-          // export default {}
-          export default {}
-        </script>\n` + `<style>div{ color: v-bind(color); }</style>`
-        ).content
-      )
-    })
-
-    test('w/ <script setup>', () => {
-      assertCode(
-        compile(
-          `<script setup>const color = 'red'</script>\n` +
-            `<style>div{ color: v-bind(color); }</style>`
-        ).content
-      )
-    })
-
-    //#4185
-    test('should ignore comments', () => {
-      const { content } = compile(
-        `<script setup>const color = 'red';const width = 100</script>\n` +
-          `<style>
-            /* comment **/
-            div{ /* color: v-bind(color); */ width:20; }
-            div{ width: v-bind(width); }
-            /* comment */
-          </style>`
-      )
-
-      expect(content).not.toMatch(`"${mockId}-color": (_setup.color)`)
-      expect(content).toMatch(`"${mockId}-width": (_setup.width)`)
-      assertCode(content)
-    })
-
-    test('w/ <script setup> using the same var multiple times', () => {
-      const { content } = compile(
-        `<script setup>
-        const color = 'red'
-        </script>\n` +
-          `<style>
-          div {
-            color: v-bind(color);
-          }
-          p {
-            color: v-bind(color);
-          }
-        </style>`
-      )
-      // color should only be injected once, even if it is twice in style
-      expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
-  "${mockId}-color": (_setup.color)
-})`)
-      assertCode(content)
-    })
-
-    test('should work with w/ complex expression', () => {
-      const { content } = compile(
-        `<script setup>
-        let a = 100
-        let b = 200
-        let foo = 300
-        </script>\n` +
-          `<style>
-          p{
-            width: calc(v-bind(foo) - 3px);
-            height: calc(v-bind('foo') - 3px);
-            top: calc(v-bind(foo + 'px') - 3px);
-          }
-          div {
-            color: v-bind((a + b) / 2 + 'px' );
-          }
-          div {
-            color: v-bind    ((a + b) / 2 + 'px' );
-          }
-          p {
-            color: v-bind(((a + b)) / (2 * a));
-          }
-        </style>`
-      )
-      expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
-  "${mockId}-foo": (_setup.foo),
-  "${mockId}-foo____px_": (_setup.foo + 'px'),
-  "${mockId}-_a___b____2____px_": ((_setup.a + _setup.b) / 2 + 'px'),
-  "${mockId}-__a___b______2___a_": (((_setup.a + _setup.b)) / (2 * _setup.a))
-})`)
-      assertCode(content)
-    })
-
-    // #6022
-    test('should be able to parse incomplete expressions', () => {
-      const { cssVars } = parse({
-        source: `<script setup>let xxx = 1</script>
-        <style scoped>
-        label {
-          font-weight: v-bind("count.toString(");
-          font-weight: v-bind(xxx);
-        }
-        </style>`
-      })
-      expect(cssVars).toMatchObject([`count.toString(`, `xxx`])
-    })
-  })
-})
diff --git a/packages/compiler-sfc/test/parseComponent.spec.ts b/packages/compiler-sfc/test/parseComponent.spec.ts
deleted file mode 100644
index 83f73483e0c..00000000000
--- a/packages/compiler-sfc/test/parseComponent.spec.ts
+++ /dev/null
@@ -1,269 +0,0 @@
-import { WarningMessage } from 'types/compiler'
-import { parseComponent } from '../src/parseComponent'
-
-describe('Single File Component parser', () => {
-  it('should parse', () => {
-    const res = parseComponent(
-      `
-      <template>
-        <div>hi</div>
-      </template>
-      <style src="./test.css"></style>
-      <style lang="stylus" scoped>
-        h1
-          color red
-        h2
-          color green
-      </style>
-      <style module>
-        h1 { font-weight: bold }
-      </style>
-      <style bool-attr val-attr="test"></style>
-      <script>
-        export default {}
-      </script>
-      <div>
-        <style>nested should be ignored</style>
-      </div>
-    `
-    )
-    expect(res.template!.content.trim()).toBe('<div>hi</div>')
-    expect(res.styles.length).toBe(4)
-    expect(res.styles[0].src).toBe('./test.css')
-    expect(res.styles[1].lang).toBe('stylus')
-    expect(res.styles[1].scoped).toBe(true)
-    expect(res.styles[1].content.trim()).toBe(
-      'h1\n  color red\nh2\n  color green'
-    )
-    expect(res.styles[2].module).toBe(true)
-    expect(res.styles[3].attrs['bool-attr']).toBe(true)
-    expect(res.styles[3].attrs['val-attr']).toBe('test')
-    expect(res.script!.content.trim()).toBe('export default {}')
-  })
-
-  it('should parse template with closed input', () => {
-    const res = parseComponent(`
-      <template>
-        <input type="text"/>
-      </template>
-    `)
-
-    expect(res.template!.content.trim()).toBe('<input type="text"/>')
-  })
-
-  it('should handle nested template', () => {
-    const res = parseComponent(`
-      <template>
-        <div><template v-if="ok">hi</template></div>
-      </template>
-    `)
-    expect(res.template!.content.trim()).toBe(
-      '<div><template v-if="ok">hi</template></div>'
-    )
-  })
-
-  it('deindent content', () => {
-    const content = `
-      <template>
-        <div></div>
-      </template>
-      <script>
-        export default {}
-      </script>
-      <style>
-        h1 { color: red }
-      </style>
-    `
-    const deindentDefault = parseComponent(content.trim(), {
-      pad: false
-    })
-    const deindentEnabled = parseComponent(content.trim(), {
-      pad: false,
-      deindent: true
-    })
-    const deindentDisabled = parseComponent(content.trim(), {
-      pad: false,
-      deindent: false
-    })
-
-    expect(deindentDefault.template!.content).toBe('\n<div></div>\n')
-    expect(deindentDefault.script!.content).toBe(
-      '\n        export default {}\n      '
-    )
-    expect(deindentDefault.styles[0].content).toBe('\nh1 { color: red }\n')
-    expect(deindentEnabled.template!.content).toBe('\n<div></div>\n')
-    expect(deindentEnabled.script!.content).toBe('\nexport default {}\n')
-    expect(deindentEnabled.styles[0].content).toBe('\nh1 { color: red }\n')
-    expect(deindentDisabled.template!.content).toBe(
-      '\n        <div></div>\n      '
-    )
-    expect(deindentDisabled.script!.content).toBe(
-      '\n        export default {}\n      '
-    )
-    expect(deindentDisabled.styles[0].content).toBe(
-      '\n        h1 { color: red }\n      '
-    )
-  })
-
-  it('pad content', () => {
-    const content = `
-      <template>
-        <div></div>
-      </template>
-      <script>
-        export default {}
-      </script>
-      <style>
-        h1 { color: red }
-      </style>
-`
-    const padDefault = parseComponent(content.trim(), {
-      pad: true,
-      deindent: true
-    })
-    const padLine = parseComponent(content.trim(), {
-      pad: 'line',
-      deindent: true
-    })
-    const padSpace = parseComponent(content.trim(), {
-      pad: 'space',
-      deindent: true
-    })
-
-    expect(padDefault.script!.content).toBe(
-      Array(3 + 1).join('//\n') + '\nexport default {}\n'
-    )
-    expect(padDefault.styles[0].content).toBe(
-      Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
-    )
-    expect(padLine.script!.content).toBe(
-      Array(3 + 1).join('//\n') + '\nexport default {}\n'
-    )
-    expect(padLine.styles[0].content).toBe(
-      Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
-    )
-    expect(padSpace.script!.content).toBe(
-      `<template>
-        <div></div>
-      </template>
-      <script>`.replace(/./g, ' ') + '\nexport default {}\n'
-    )
-    expect(padSpace.styles[0].content).toBe(
-      `<template>
-        <div></div>
-      </template>
-      <script>
-        export default {}
-      </script>
-      <style>`.replace(/./g, ' ') + '\nh1 { color: red }\n'
-    )
-  })
-
-  it('should handle template blocks with lang as special text', () => {
-    const res = parseComponent(
-      `
-      <template lang="pug">
-        div
-          h1(v-if='1 < 2') hello
-      </template>
-    `,
-      { deindent: true }
-    )
-    expect(res.template!.content.trim()).toBe(`div\n  h1(v-if='1 < 2') hello`)
-  })
-
-  it('should handle component contains "<" only', () => {
-    const res = parseComponent(`
-      <template>
-        <span><</span>
-      </template>
-    `)
-    expect(res.template!.content.trim()).toBe(`<span><</span>`)
-  })
-
-  it('should handle custom blocks without parsing them', () => {
-    const res = parseComponent(
-      `
-      <template>
-        <div></div>
-      </template>
-      <example name="simple">
-        <my-button ref="button">Hello</my-button>
-      </example>
-      <example name="with props">
-        <my-button color="red">Hello</my-button>
-      </example>
-      <test name="simple" foo="bar">
-      export default function simple (vm) {
-        describe('Hello', () => {
-          it('should display Hello', () => {
-            this.vm.$refs.button.$el.innerText.should.equal('Hello')
-          }))
-        }))
-      }
-      </test>
-      <custom src="./x.json"></custom>
-    `
-    )
-    expect(res.customBlocks.length).toBe(4)
-
-    const simpleExample = res.customBlocks[0]
-    expect(simpleExample.type).toBe('example')
-    expect(simpleExample.content.trim()).toBe(
-      '<my-button ref="button">Hello</my-button>'
-    )
-    expect(simpleExample.attrs.name).toBe('simple')
-
-    const withProps = res.customBlocks[1]
-    expect(withProps.type).toBe('example')
-    expect(withProps.content.trim()).toBe(
-      '<my-button color="red">Hello</my-button>'
-    )
-    expect(withProps.attrs.name).toBe('with props')
-
-    const simpleTest = res.customBlocks[2]
-    expect(simpleTest.type).toBe('test')
-    expect(simpleTest.content.trim())
-      .toBe(`export default function simple (vm) {
-  describe('Hello', () => {
-    it('should display Hello', () => {
-      this.vm.$refs.button.$el.innerText.should.equal('Hello')
-    }))
-  }))
-}`)
-    expect(simpleTest.attrs.name).toBe('simple')
-    expect(simpleTest.attrs.foo).toBe('bar')
-
-    const customWithSrc = res.customBlocks[3]
-    expect(customWithSrc.src).toBe('./x.json')
-  })
-
-  // Regression #4289
-  it('accepts nested template tag', () => {
-    const raw = `<div>
-      <template v-if="true === true">
-        <section class="section">
-          <div class="container">
-            Should be shown
-          </div>
-        </section>
-      </template>
-      <template v-else>
-        <p>Should not be shown</p>
-      </template>
-    </div>`
-    const res = parseComponent(`<template>${raw}</template>`)
-    expect(res.template!.content.trim()).toBe(raw)
-  })
-
-  it('should not hang on trailing text', () => {
-    const res = parseComponent(`<template>hi</`)
-    expect(res.template!.content).toBe('hi')
-  })
-
-  it('should collect errors with source range', () => {
-    const res = parseComponent(`<template>hi</`, { outputSourceRange: true })
-    expect(res.errors.length).toBe(1)
-    expect((res.errors[0] as WarningMessage).start).toBe(0)
-  })
-})
diff --git a/packages/compiler-sfc/test/prefixIdentifiers.spec.ts b/packages/compiler-sfc/test/prefixIdentifiers.spec.ts
deleted file mode 100644
index 2eb05c8c0d1..00000000000
--- a/packages/compiler-sfc/test/prefixIdentifiers.spec.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import { prefixIdentifiers } from '../src/prefixIdentifiers'
-import { compile } from 'web/entry-compiler'
-import { format } from 'prettier'
-import { BindingTypes } from '../src/types'
-
-const toFn = (source: string) => `function render(){${source}\n}`
-
-it('should work', () => {
-  const { render } = compile(`<div id="app">
-  <div :style="{ color }">{{ foo }}</div>
-  <p v-for="i in list">{{ i }}</p>
-  <foo inline-template>
-    <div>{{ bar }}</div>
-  </foo>
-</div>`)
-
-  const result = format(prefixIdentifiers(toFn(render)), {
-    semi: false,
-    parser: 'babel'
-  })
-
-  expect(result).not.toMatch(`_vm._c`)
-  expect(result).toMatch(`_vm.foo`)
-  expect(result).toMatch(`_vm.list`)
-  expect(result).toMatch(`{ color: _vm.color }`)
-  expect(result).not.toMatch(`_vm.i`)
-  expect(result).not.toMatch(`with (this)`)
-
-  expect(result).toMatchInlineSnapshot(`
-    "function render() {
-      var _vm = this,
-        _c = _vm._self._c
-      return _c(
-        "div",
-        { attrs: { id: "app" } },
-        [
-          _c("div", { style: { color: _vm.color } }, [_vm._v(_vm._s(_vm.foo))]),
-          _vm._v(" "),
-          _vm._l(_vm.list, function (i) {
-            return _c("p", [_vm._v(_vm._s(i))])
-          }),
-          _vm._v(" "),
-          _c("foo", {
-            inlineTemplate: {
-              render: function () {
-                var _vm = this,
-                  _c = _vm._self._c
-                return _c("div", [_vm._v(_vm._s(_vm.bar))])
-              },
-              staticRenderFns: [],
-            },
-          }),
-        ],
-        2
-      )
-    }
-    "
-  `)
-})
-
-it('setup bindings', () => {
-  const { render } = compile(`<div @click="count++">{{ count }}</div>`)
-
-  const result = format(
-    prefixIdentifiers(toFn(render), false, false, undefined, {
-      count: BindingTypes.SETUP_REF
-    }),
-    {
-      semi: false,
-      parser: 'babel'
-    }
-  )
-
-  expect(result).toMatch(`_setup = _vm._self._setupProxy`)
-  expect(result).toMatch(`_setup.count++`)
-  expect(result).toMatch(`_vm._s(_setup.count)`)
-
-  expect(result).toMatchInlineSnapshot(`
-    "function render() {
-      var _vm = this,
-        _c = _vm._self._c,
-        _setup = _vm._self._setupProxy
-      return _c(
-        "div",
-        {
-          on: {
-            click: function ($event) {
-              _setup.count++
-            },
-          },
-        },
-        [_vm._v(_vm._s(_setup.count))]
-      )
-    }
-    "
-  `)
-})
diff --git a/packages/compiler-sfc/test/rewriteDefault.spec.ts b/packages/compiler-sfc/test/rewriteDefault.spec.ts
deleted file mode 100644
index fd9e4ef4def..00000000000
--- a/packages/compiler-sfc/test/rewriteDefault.spec.ts
+++ /dev/null
@@ -1,311 +0,0 @@
-import { rewriteDefault } from '../src'
-
-describe('compiler sfc: rewriteDefault', () => {
-  test('without export default', () => {
-    expect(rewriteDefault(`export  a = {}`, 'script')).toMatchInlineSnapshot(`
-      "export  a = {}
-      const script = {}"
-    `)
-  })
-
-  test('rewrite export default', () => {
-    expect(
-      rewriteDefault(`export  default {}`, 'script')
-    ).toMatchInlineSnapshot(`"const script = {}"`)
-  })
-
-  test('rewrite export named default', () => {
-    expect(
-      rewriteDefault(
-        `const a = 1 \n export { a as b, a as default, a as c}`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-      "const a = 1 
-       export { a as b,  a as c}
-      const script = a"
-    `)
-
-    expect(
-      rewriteDefault(
-        `const a = 1 \n export { a as b, a as default    , a as c}`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-      "const a = 1 
-       export { a as b,  a as c}
-      const script = a"
-    `)
-  })
-
-  test('w/ comments', async () => {
-    expect(rewriteDefault(`// export default\nexport default {}`, 'script'))
-      .toMatchInlineSnapshot(`
-      "// export default
-      const script = {}"
-    `)
-  })
-
-  test('export named default multiline', () => {
-    expect(
-      rewriteDefault(`let App = {}\n export {\nApp as default\n}`, '_sfc_main')
-    ).toMatchInlineSnapshot(`
-      "let App = {}
-       export {
-      
-      }
-      const _sfc_main = App"
-    `)
-  })
-
-  test('export named default multiline /w comments', () => {
-    expect(
-      rewriteDefault(
-        `const a = 1 \n export {\n a as b,\n a as default,\n a as c}\n` +
-          `// export { myFunction as default }`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-      "const a = 1 
-       export {
-       a as b,
-       
-       a as c}
-      // export { myFunction as default }
-      const script = a"
-    `)
-
-    expect(
-      rewriteDefault(
-        `const a = 1 \n export {\n a as b,\n a as default      ,\n a as c}\n` +
-          `// export { myFunction as default }`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-      "const a = 1 
-       export {
-       a as b,
-       
-       a as c}
-      // export { myFunction as default }
-      const script = a"
-    `)
-  })
-
-  test(`export { default } from '...'`, async () => {
-    expect(
-      rewriteDefault(`export { default, foo } from './index.js'`, 'script')
-    ).toMatchInlineSnapshot(`
-    "import { default as __VUE_DEFAULT__ } from './index.js'
-    export {  foo } from './index.js'
-    const script = __VUE_DEFAULT__"
-    `)
-
-    expect(
-      rewriteDefault(`export { default    , foo } from './index.js'`, 'script')
-    ).toMatchInlineSnapshot(`
-    "import { default as __VUE_DEFAULT__ } from './index.js'
-    export {  foo } from './index.js'
-    const script = __VUE_DEFAULT__"
-    `)
-
-    expect(
-      rewriteDefault(`export { foo,   default } from './index.js'`, 'script')
-    ).toMatchInlineSnapshot(`
-    "import { default as __VUE_DEFAULT__ } from './index.js'
-    export { foo,    } from './index.js'
-    const script = __VUE_DEFAULT__"
-    `)
-
-    expect(
-      rewriteDefault(
-        `export { foo as default, bar } from './index.js'`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-    "import { foo } from './index.js'
-    export {  bar } from './index.js'
-    const script = foo"
-    `)
-
-    expect(
-      rewriteDefault(
-        `export { foo as default     , bar } from './index.js'`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-    "import { foo } from './index.js'
-    export {  bar } from './index.js'
-    const script = foo"
-    `)
-
-    expect(
-      rewriteDefault(
-        `export { bar,   foo as default } from './index.js'`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-    "import { foo } from './index.js'
-    export { bar,    } from './index.js'
-    const script = foo"
-    `)
-  })
-
-  test('export default class', async () => {
-    expect(rewriteDefault(`export default class Foo {}`, 'script'))
-      .toMatchInlineSnapshot(`
-      "class Foo {}
-      const script = Foo"
-    `)
-  })
-
-  test('export default class w/ comments', async () => {
-    expect(
-      rewriteDefault(`// export default\nexport default class Foo {}`, 'script')
-    ).toMatchInlineSnapshot(`
-      "// export default
-      class Foo {}
-      const script = Foo"
-    `)
-  })
-
-  test('export default class w/ comments 2', async () => {
-    expect(
-      rewriteDefault(
-        `export default {}\n` + `// export default class Foo {}`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-      "const script = {}
-      // export default class Foo {}"
-    `)
-  })
-
-  test('export default class w/ comments 3', async () => {
-    expect(
-      rewriteDefault(
-        `/*\nexport default class Foo {}*/\n` + `export default class Bar {}`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-      "/*
-      export default class Foo {}*/
-       class Bar {}
-      const script = Bar"
-    `)
-  })
-
-  test('@Component\nexport default class', async () => {
-    expect(rewriteDefault(`@Component\nexport default class Foo {}`, 'script'))
-      .toMatchInlineSnapshot(`
-      "@Component
-      class Foo {}
-      const script = Foo"
-    `)
-  })
-
-  test('@Component\nexport default class w/ comments', async () => {
-    expect(
-      rewriteDefault(
-        `// export default\n@Component\nexport default class Foo {}`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-      "// export default
-      @Component
-      class Foo {}
-      const script = Foo"
-    `)
-  })
-
-  test('@Component\nexport default class w/ comments 2', async () => {
-    expect(
-      rewriteDefault(
-        `export default {}\n` + `// @Component\n// export default class Foo {}`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-      "const script = {}
-      // @Component
-      // export default class Foo {}"
-    `)
-  })
-
-  test('@Component\nexport default class w/ comments 3', async () => {
-    expect(
-      rewriteDefault(
-        `/*\n@Component\nexport default class Foo {}*/\n` +
-          `export default class Bar {}`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-      "/*
-      @Component
-      export default class Foo {}*/
-       class Bar {}
-      const script = Bar"
-    `)
-  })
-
-  // #13060
-  test('@Component\nexport default class w/ comments 4', async () => {
-    expect(
-      rewriteDefault(
-        `@Component
-        export default class App extends Vue {
-          /* default <- This word means my component is not built correctly */
-          @Prop({ type: String, required: true })
-          protected someString: string;
-        }`,
-        'script'
-      )
-    ).toMatchInlineSnapshot(`
-      "@Component
-              class App extends Vue {
-                /* default <- This word means my component is not built correctly */
-                @Prop({ type: String, required: true })
-                protected someString: string;
-              }
-      const script = App"
-    `)
-  })
-
-  // #12892
-  test('@Component\nexport default class w/ comments 5', async () => {
-    expect(
-      rewriteDefault(
-        `@Component({})
-        export default class HelloWorld extends Vue {
-          test = "";
-          mounted() {
-            console.log("mounted!");
-            this.test = "Hallo Welt!";
-          }
-          exportieren(): void {
-            // do nothing
-          }
-          defaultWert(): void {
-            // do nothing
-          }
-        }`,
-        'script',
-        ['typescript', 'decorators-legacy']
-      )
-    ).toMatchInlineSnapshot(`
-      "@Component({}) class HelloWorld extends Vue {
-                test = "";
-                mounted() {
-                  console.log("mounted!");
-                  this.test = "Hallo Welt!";
-                }
-                exportieren(): void {
-                  // do nothing
-                }
-                defaultWert(): void {
-                  // do nothing
-                }
-              }
-      const script = HelloWorld"
-    `)
-  })
-})
diff --git a/packages/compiler-sfc/test/stylePluginScoped.spec.ts b/packages/compiler-sfc/test/stylePluginScoped.spec.ts
deleted file mode 100644
index 46308e37466..00000000000
--- a/packages/compiler-sfc/test/stylePluginScoped.spec.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import { compileStyle } from '../src/compileStyle'
-
-// vue-loader/#1370
-test('spaces after selector', () => {
-  const { code } = compileStyle({
-    source: `.foo , .bar { color: red; }`,
-    filename: 'test.css',
-    id: 'test'
-  })
-
-  expect(code).toMatch(`.foo[test], .bar[test] { color: red;`)
-})
-
-test('leading deep selector', () => {
-  const { code } = compileStyle({
-    source: `>>> .foo { color: red; }`,
-    filename: 'test.css',
-    id: 'test'
-  })
-
-  expect(code).toMatch(`[test] .foo { color: red;`)
-})
-
-test('scoped css', () => {
-  const { code: style } = compileStyle({
-    id: 'v-scope-xxx',
-    scoped: true,
-    filename: 'example.vue',
-    source: `
-.test {
-  color: yellow;
-}
-.test:after {
-  content: 'bye!';
-}
-h1 {
-  color: green;
-}
-.anim {
-  animation: color 5s infinite, other 5s;
-}
-.anim-2 {
-  animation-name: color;
-  animation-duration: 5s;
-}
-.anim-3 {
-  animation: 5s color infinite, 5s other;
-}
-.anim-multiple {
-  animation: color 5s infinite, opacity 2s;
-}
-.anim-multiple-2 {
-  animation-name: color, opacity;
-  animation-duration: 5s, 2s;
-}
-
-@keyframes color {
-  from { color: red; }
-  to { color: green; }
-}
-@-webkit-keyframes color {
-  from { color: red; }
-  to { color: green; }
-}
-@keyframes opacity {
-  from { opacity: 0; }
-  to { opacity: 1; }
-}
-@-webkit-keyframes opacity {
-  from { opacity: 0; }
-  to { opacity: 1; }
-}
-.foo p >>> .bar {
-  color: red;
-}
-.foo div /deep/ .bar {
-  color: red;
-}
-
-.foo span ::v-deep .bar {
-  color: red;
-}
-`
-  })
-
-  expect(style).toContain(`.test[v-scope-xxx] {\n  color: yellow;\n}`)
-  expect(style).toContain(`.test[v-scope-xxx]:after {\n  content: \'bye!\';\n}`)
-  expect(style).toContain(`h1[v-scope-xxx] {\n  color: green;\n}`)
-  // scoped keyframes
-  expect(style).toContain(
-    `.anim[v-scope-xxx] {\n  animation: color-v-scope-xxx 5s infinite, other 5s;`
-  )
-  expect(style).toContain(
-    `.anim-2[v-scope-xxx] {\n  animation-name: color-v-scope-xxx`
-  )
-  expect(style).toContain(
-    `.anim-3[v-scope-xxx] {\n  animation: 5s color-v-scope-xxx infinite, 5s other;`
-  )
-  expect(style).toContain(`@keyframes color-v-scope-xxx {`)
-  expect(style).toContain(`@-webkit-keyframes color-v-scope-xxx {`)
-
-  expect(style).toContain(
-    `.anim-multiple[v-scope-xxx] {\n  animation: color-v-scope-xxx 5s infinite,opacity-v-scope-xxx 2s;`
-  )
-  expect(style).toContain(
-    `.anim-multiple-2[v-scope-xxx] {\n  animation-name: color-v-scope-xxx,opacity-v-scope-xxx;`
-  )
-  expect(style).toContain(`@keyframes opacity-v-scope-xxx {`)
-  expect(style).toContain(`@-webkit-keyframes opacity-v-scope-xxx {`)
-  // >>> combinator
-  expect(style).toContain(`.foo p[v-scope-xxx] .bar {\n  color: red;\n}`)
-  // /deep/ alias for >>>
-  expect(style).toContain(`.foo div[v-scope-xxx] .bar {\n  color: red;\n}`)
-  // ::-v-deep alias for >>>
-  expect(style).toContain(`.foo span[v-scope-xxx] .bar {\n  color: red;\n}`)
-})
-
-test('pseudo element', () => {
-  const { code } = compileStyle({
-    source: '::selection { display: none; }',
-    filename: 'test.css',
-    id: 'test'
-  })
-
-  expect(code).toContain('[test]::selection {')
-})
-
-test('spaces before pseudo element', () => {
-  const { code } = compileStyle({
-    source: '.abc, ::selection { color: red; }',
-    filename: 'test.css',
-    id: 'test'
-  })
-
-  expect(code).toContain('.abc[test],')
-  expect(code).toContain('[test]::selection {')
-})
diff --git a/packages/compiler-sfc/test/tsconfig.json b/packages/compiler-sfc/test/tsconfig.json
deleted file mode 100644
index c0fca46c617..00000000000
--- a/packages/compiler-sfc/test/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "extends": "../../../tsconfig.json",
-  "compilerOptions": {
-    "types": ["node", "vitest/globals"]
-  },
-  "include": ["../src", "."]
-}
diff --git a/packages/compiler-sfc/test/util.ts b/packages/compiler-sfc/test/util.ts
deleted file mode 100644
index 773158999de..00000000000
--- a/packages/compiler-sfc/test/util.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import {
-  parse,
-  compileScript,
-  type SFCParseOptions,
-  type SFCScriptCompileOptions
-} from '../src'
-import { parse as babelParse } from '@babel/parser'
-
-export const mockId = 'xxxxxxxx'
-
-export function compile(
-  source: string,
-  options?: Partial<SFCScriptCompileOptions>,
-  parseOptions?: Partial<SFCParseOptions>
-) {
-  const sfc = parse({
-    ...parseOptions,
-    source
-  })
-  return compileScript(sfc, { id: mockId, ...options })
-}
-
-export function assertCode(code: string) {
-  // parse the generated code to make sure it is valid
-  try {
-    babelParse(code, {
-      sourceType: 'module',
-      plugins: ['typescript']
-    })
-  } catch (e: any) {
-    console.log(code)
-    throw e
-  }
-  expect(code).toMatchSnapshot()
-}
diff --git a/packages/server-renderer/README.md b/packages/server-renderer/README.md
deleted file mode 100644
index 2921c51eadc..00000000000
--- a/packages/server-renderer/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# vue-server-renderer
-
-> 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).
-
-This package offers Node.js server-side rendering for Vue 2.0.
-
-- [API Reference](https://ssr.vuejs.org/en/api.html)
-- [Vue.js Server-Side Rendering Guide](https://ssr.vuejs.org)
diff --git a/packages/server-renderer/client-plugin.d.ts b/packages/server-renderer/client-plugin.d.ts
deleted file mode 100644
index d0d639a6463..00000000000
--- a/packages/server-renderer/client-plugin.d.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { WebpackPlugin } from './types/plugin'
-declare const Plugin: WebpackPlugin
-export = Plugin
diff --git a/packages/server-renderer/index.js b/packages/server-renderer/index.js
deleted file mode 100644
index f3a053cf6b1..00000000000
--- a/packages/server-renderer/index.js
+++ /dev/null
@@ -1,26 +0,0 @@
-try {
-  var vueVersion = require('vue').version
-} catch (e) {}
-
-var packageName = require('./package.json').name
-var packageVersion = require('./package.json').version
-if (vueVersion && vueVersion !== packageVersion) {
-  throw new Error(
-    '\n\nVue packages version mismatch:\n\n' +
-      '- vue@' +
-      vueVersion +
-      '\n' +
-      '- ' +
-      packageName +
-      '@' +
-      packageVersion +
-      '\n\n' +
-      'This may cause things to work incorrectly. Make sure to use the same version for both.\n'
-  )
-}
-
-if (process.env.NODE_ENV === 'production') {
-  module.exports = require('./build.prod.js')
-} else {
-  module.exports = require('./build.dev.js')
-}
diff --git a/packages/server-renderer/package.json b/packages/server-renderer/package.json
deleted file mode 100644
index 89da3496f13..00000000000
--- a/packages/server-renderer/package.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
-  "name": "vue-server-renderer",
-  "version": "2.7.16",
-  "description": "server renderer for Vue 2.0",
-  "main": "index.js",
-  "types": "types/index.d.ts",
-  "repository": {
-    "type": "git",
-    "url": "git+https://github.com/vuejs/vue.git"
-  },
-  "keywords": [
-    "vue",
-    "server",
-    "ssr"
-  ],
-  "files": [
-    "types/*.d.ts",
-    "*.js",
-    "*.d.ts"
-  ],
-  "author": "Evan You",
-  "license": "MIT",
-  "bugs": {
-    "url": "https://github.com/vuejs/vue/issues"
-  },
-  "dependencies": {
-    "chalk": "^4.1.2",
-    "hash-sum": "^2.0.0",
-    "he": "^1.2.0",
-    "lodash.template": "^4.5.0",
-    "lodash.uniq": "^4.5.0",
-    "resolve": "^1.22.0",
-    "serialize-javascript": "^6.0.0",
-    "source-map": "0.5.6"
-  },
-  "devDependencies": {
-    "@types/webpack": "^4.41.32",
-    "file-loader": "^3.0.1",
-    "memory-fs": "^0.5.0",
-    "vue": "file:../..",
-    "webpack": "^4.47.0"
-  },
-  "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer#readme"
-}
diff --git a/packages/server-renderer/server-plugin.d.ts b/packages/server-renderer/server-plugin.d.ts
deleted file mode 100644
index d0d639a6463..00000000000
--- a/packages/server-renderer/server-plugin.d.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { WebpackPlugin } from './types/plugin'
-declare const Plugin: WebpackPlugin
-export = Plugin
diff --git a/packages/server-renderer/src/bundle-renderer/create-bundle-renderer.ts b/packages/server-renderer/src/bundle-renderer/create-bundle-renderer.ts
deleted file mode 100644
index d7587321976..00000000000
--- a/packages/server-renderer/src/bundle-renderer/create-bundle-renderer.ts
+++ /dev/null
@@ -1,159 +0,0 @@
-import { createPromiseCallback } from '../util'
-import { createBundleRunner } from './create-bundle-runner'
-import type { Renderer, RenderOptions } from '../create-renderer'
-import {
-  createSourceMapConsumers,
-  rewriteErrorTrace
-} from './source-map-support'
-
-const fs = require('fs')
-const path = require('path')
-const PassThrough = require('stream').PassThrough
-
-const INVALID_MSG =
-  'Invalid server-rendering bundle format. Should be a string ' +
-  'or a bundle Object of type:\n\n' +
-  `{
-  entry: string;
-  files: { [filename: string]: string; };
-  maps: { [filename: string]: string; };
-}\n`
-
-// The render bundle can either be a string (single bundled file)
-// or a bundle manifest object generated by vue-ssr-webpack-plugin.
-type RenderBundle = {
-  basedir?: string
-  entry: string
-  files: { [filename: string]: string }
-  maps: { [filename: string]: string }
-  modules?: { [filename: string]: Array<string> }
-}
-
-export function createBundleRendererCreator(
-  createRenderer: (options?: RenderOptions) => Renderer
-) {
-  return function createBundleRenderer(
-    bundle: string | RenderBundle,
-    rendererOptions: RenderOptions = {}
-  ) {
-    let files, entry, maps
-    let basedir = rendererOptions.basedir
-
-    // load bundle if given filepath
-    if (
-      typeof bundle === 'string' &&
-      /\.js(on)?$/.test(bundle) &&
-      path.isAbsolute(bundle)
-    ) {
-      if (fs.existsSync(bundle)) {
-        const isJSON = /\.json$/.test(bundle)
-        basedir = basedir || path.dirname(bundle)
-        bundle = fs.readFileSync(bundle, 'utf-8')
-        if (isJSON) {
-          try {
-            // @ts-expect-error
-            bundle = JSON.parse(bundle)
-          } catch (e: any) {
-            throw new Error(`Invalid JSON bundle file: ${bundle}`)
-          }
-        }
-      } else {
-        throw new Error(`Cannot locate bundle file: ${bundle}`)
-      }
-    }
-
-    if (typeof bundle === 'object') {
-      entry = bundle.entry
-      files = bundle.files
-      basedir = basedir || bundle.basedir
-      maps = createSourceMapConsumers(bundle.maps)
-      if (typeof entry !== 'string' || typeof files !== 'object') {
-        throw new Error(INVALID_MSG)
-      }
-    } else if (typeof bundle === 'string') {
-      entry = '__vue_ssr_bundle__'
-      files = { __vue_ssr_bundle__: bundle }
-      maps = {}
-    } else {
-      throw new Error(INVALID_MSG)
-    }
-
-    const renderer = createRenderer(rendererOptions)
-
-    const run = createBundleRunner(
-      entry,
-      files,
-      basedir,
-      rendererOptions.runInNewContext
-    )
-
-    return {
-      renderToString: (context?: Object | undefined, cb?: any) => {
-        if (typeof context === 'function') {
-          cb = context
-          context = {}
-        }
-
-        let promise
-        if (!cb) {
-          ;({ promise, cb } = createPromiseCallback())
-        }
-
-        run(context)
-          .catch(err => {
-            rewriteErrorTrace(err, maps)
-            cb(err)
-          })
-          .then(app => {
-            if (app) {
-              //@ts-expect-error
-              renderer.renderToString(app, context, (err, res) => {
-                rewriteErrorTrace(err, maps)
-                cb(err, res)
-              })
-            }
-          })
-
-        return promise
-      },
-
-      renderToStream: (context?: Object) => {
-        const res = new PassThrough()
-        run(context)
-          .catch(err => {
-            rewriteErrorTrace(err, maps)
-            // avoid emitting synchronously before user can
-            // attach error listener
-            process.nextTick(() => {
-              res.emit('error', err)
-            })
-          })
-          .then(app => {
-            if (app) {
-              //@ts-expect-error
-              const renderStream = renderer.renderToStream(app, context)
-
-              renderStream.on('error', err => {
-                rewriteErrorTrace(err, maps)
-                res.emit('error', err)
-              })
-
-              // relay HTMLStream special events
-              if (rendererOptions && rendererOptions.template) {
-                renderStream.on('beforeStart', () => {
-                  res.emit('beforeStart')
-                })
-                renderStream.on('beforeEnd', () => {
-                  res.emit('beforeEnd')
-                })
-              }
-
-              renderStream.pipe(res)
-            }
-          })
-
-        return res
-      }
-    }
-  }
-}
diff --git a/packages/server-renderer/src/bundle-renderer/create-bundle-runner.ts b/packages/server-renderer/src/bundle-renderer/create-bundle-runner.ts
deleted file mode 100644
index 6e9d063693b..00000000000
--- a/packages/server-renderer/src/bundle-renderer/create-bundle-runner.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-import { isPlainObject } from 'shared/util'
-
-const vm = require('vm')
-const path = require('path')
-const resolve = require('resolve')
-const NativeModule = require('module')
-
-function createSandbox(context?: any) {
-  const sandbox = {
-    Buffer,
-    console,
-    process,
-    setTimeout,
-    setInterval,
-    setImmediate,
-    clearTimeout,
-    clearInterval,
-    clearImmediate,
-    __VUE_SSR_CONTEXT__: context
-  }
-
-  // @ts-expect-error
-  sandbox.global = sandbox
-  return sandbox
-}
-
-function compileModule(files, basedir, runInNewContext) {
-  const compiledScripts = {}
-  const resolvedModules = {}
-
-  function getCompiledScript(filename) {
-    if (compiledScripts[filename]) {
-      return compiledScripts[filename]
-    }
-    const code = files[filename]
-    const wrapper = NativeModule.wrap(code)
-    const script = new vm.Script(wrapper, {
-      filename,
-      displayErrors: true
-    })
-    compiledScripts[filename] = script
-    return script
-  }
-
-  function evaluateModule(filename, sandbox, evaluatedFiles = {}) {
-    if (evaluatedFiles[filename]) {
-      return evaluatedFiles[filename]
-    }
-
-    const script = getCompiledScript(filename)
-    const compiledWrapper =
-      runInNewContext === false
-        ? script.runInThisContext()
-        : script.runInNewContext(sandbox)
-    const m = { exports: {} }
-    const r = file => {
-      file = path.posix.join('.', file)
-      if (files[file]) {
-        return evaluateModule(file, sandbox, evaluatedFiles)
-      } else if (basedir) {
-        return require(resolvedModules[file] ||
-          (resolvedModules[file] = resolve.sync(file, { basedir })))
-      } else {
-        return require(file)
-      }
-    }
-    compiledWrapper.call(m.exports, m.exports, r, m)
-
-    const res = Object.prototype.hasOwnProperty.call(m.exports, 'default')
-      ? // @ts-expect-error
-        m.exports.default
-      : m.exports
-    evaluatedFiles[filename] = res
-    return res
-  }
-  return evaluateModule
-}
-
-function deepClone(val) {
-  if (isPlainObject(val)) {
-    const res = {}
-    for (const key in val) {
-      res[key] = deepClone(val[key])
-    }
-    return res
-  } else if (Array.isArray(val)) {
-    return val.slice()
-  } else {
-    return val
-  }
-}
-
-export function createBundleRunner(entry, files, basedir, runInNewContext) {
-  const evaluate = compileModule(files, basedir, runInNewContext)
-  if (runInNewContext !== false && runInNewContext !== 'once') {
-    // new context mode: creates a fresh context and re-evaluate the bundle
-    // on each render. Ensures entire application state is fresh for each
-    // render, but incurs extra evaluation cost.
-    return (userContext = {}) =>
-      new Promise(resolve => {
-        // @ts-expect-error
-        userContext._registeredComponents = new Set()
-        const res = evaluate(entry, createSandbox(userContext))
-        resolve(typeof res === 'function' ? res(userContext) : res)
-      })
-  } else {
-    // direct mode: instead of re-evaluating the whole bundle on
-    // each render, it simply calls the exported function. This avoids the
-    // module evaluation costs but requires the source code to be structured
-    // slightly differently.
-    let runner // lazy creation so that errors can be caught by user
-    let initialContext
-    return (userContext = {}) =>
-      new Promise(resolve => {
-        if (!runner) {
-          const sandbox = runInNewContext === 'once' ? createSandbox() : global
-          // the initial context is only used for collecting possible non-component
-          // styles injected by vue-style-loader.
-          // @ts-expect-error
-          initialContext = sandbox.__VUE_SSR_CONTEXT__ = {}
-          runner = evaluate(entry, sandbox)
-          // On subsequent renders, __VUE_SSR_CONTEXT__ will not be available
-          // to prevent cross-request pollution.
-          // @ts-expect-error
-          delete sandbox.__VUE_SSR_CONTEXT__
-          if (typeof runner !== 'function') {
-            throw new Error(
-              'bundle export should be a function when using ' +
-                '{ runInNewContext: false }.'
-            )
-          }
-        }
-        // @ts-expect-error
-        userContext._registeredComponents = new Set()
-
-        // vue-style-loader styles imported outside of component lifecycle hooks
-        if (initialContext._styles) {
-          // @ts-expect-error
-          userContext._styles = deepClone(initialContext._styles)
-          // #6353 ensure "styles" is exposed even if no styles are injected
-          // in component lifecycles.
-          // the renderStyles fn is exposed by vue-style-loader >= 3.0.3
-          const renderStyles = initialContext._renderStyles
-          if (renderStyles) {
-            Object.defineProperty(userContext, 'styles', {
-              enumerable: true,
-              get() {
-                // @ts-expect-error
-                return renderStyles(userContext._styles)
-              }
-            })
-          }
-        }
-
-        resolve(runner(userContext))
-      })
-  }
-}
diff --git a/packages/server-renderer/src/bundle-renderer/source-map-support.ts b/packages/server-renderer/src/bundle-renderer/source-map-support.ts
deleted file mode 100644
index 6f03ebb131a..00000000000
--- a/packages/server-renderer/src/bundle-renderer/source-map-support.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-const SourceMapConsumer = require('source-map').SourceMapConsumer
-
-const filenameRE = /\(([^)]+\.js):(\d+):(\d+)\)$/
-
-export function createSourceMapConsumers(rawMaps: Object) {
-  const maps = {}
-  Object.keys(rawMaps).forEach(file => {
-    maps[file] = new SourceMapConsumer(rawMaps[file])
-  })
-  return maps
-}
-
-export function rewriteErrorTrace(
-  e: any,
-  mapConsumers: {
-    [key: string]: typeof SourceMapConsumer
-  }
-) {
-  if (e && typeof e.stack === 'string') {
-    e.stack = e.stack
-      .split('\n')
-      .map(line => {
-        return rewriteTraceLine(line, mapConsumers)
-      })
-      .join('\n')
-  }
-}
-
-function rewriteTraceLine(
-  trace: string,
-  mapConsumers: {
-    [key: string]: typeof SourceMapConsumer
-  }
-) {
-  const m = trace.match(filenameRE)
-  const map = m && mapConsumers[m[1]]
-  if (m != null && map) {
-    const originalPosition = map.originalPositionFor({
-      line: Number(m[2]),
-      column: Number(m[3])
-    })
-    if (originalPosition.source != null) {
-      const { source, line, column } = originalPosition
-      const mappedPosition = `(${source.replace(
-        /^webpack:\/\/\//,
-        ''
-      )}:${String(line)}:${String(column)})`
-      return trace.replace(filenameRE, mappedPosition)
-    } else {
-      return trace
-    }
-  } else {
-    return trace
-  }
-}
diff --git a/packages/server-renderer/src/compiler.ts b/packages/server-renderer/src/compiler.ts
deleted file mode 100644
index 094ac929e90..00000000000
--- a/packages/server-renderer/src/compiler.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { baseOptions } from 'web/compiler/options'
-import { createCompiler } from 'server/optimizing-compiler/index'
-
-const { compile, compileToFunctions } = createCompiler(baseOptions)
-
-export { compile as ssrCompile, compileToFunctions as ssrCompileToFunctions }
diff --git a/packages/server-renderer/src/create-basic-renderer.ts b/packages/server-renderer/src/create-basic-renderer.ts
deleted file mode 100644
index 05f5509b4cf..00000000000
--- a/packages/server-renderer/src/create-basic-renderer.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { createWriteFunction } from './write'
-import { createRenderFunction } from './render'
-import type { RenderOptions } from './create-renderer'
-import type { Component } from 'types/component'
-
-export function createBasicRenderer({
-  modules = [],
-  directives = {},
-  isUnaryTag = () => false,
-  cache
-}: RenderOptions = {}) {
-  const render = createRenderFunction(modules, directives, isUnaryTag, cache)
-
-  return function renderToString(
-    component: Component,
-    context?: any,
-    done?: any
-  ): void {
-    if (typeof context === 'function') {
-      done = context
-      context = {}
-    }
-    let result = ''
-    const write = createWriteFunction(text => {
-      result += text
-      return false
-    }, done)
-    try {
-      //@ts-expect-error
-      render(component, write, context, () => {
-        done(null, result)
-      })
-    } catch (e: any) {
-      done(e)
-    }
-  }
-}
diff --git a/packages/server-renderer/src/create-renderer.ts b/packages/server-renderer/src/create-renderer.ts
deleted file mode 100644
index a4b992eb8c6..00000000000
--- a/packages/server-renderer/src/create-renderer.ts
+++ /dev/null
@@ -1,164 +0,0 @@
-import RenderStream from './render-stream'
-import { createWriteFunction } from './write'
-import { createRenderFunction } from './render'
-import { createPromiseCallback } from './util'
-import TemplateRenderer from './template-renderer/index'
-import type { ClientManifest } from './template-renderer/index'
-import type { Component } from 'types/component'
-import VNode from 'core/vdom/vnode'
-import { Readable } from 'stream'
-
-export type Renderer = {
-  renderToString: (
-    component: Component,
-    context?: any,
-    cb?: any
-  ) => Promise<string> | undefined
-  renderToStream: (component: Component, context?: Object) => Readable
-}
-
-type RenderCache = {
-  get: (key: string, cb?: Function) => string | void
-  set: (key: string, val: string) => void
-  has?: (key: string, cb?: Function) => boolean | void
-}
-
-export type RenderOptions = {
-  modules?: Array<(vnode: VNode) => string | null>
-  directives?: Object
-  isUnaryTag?: Function
-  cache?: RenderCache
-  template?:
-    | string
-    | ((content: string, context: any) => string | Promise<string>)
-  inject?: boolean
-  basedir?: string
-  shouldPreload?: Function
-  shouldPrefetch?: Function
-  clientManifest?: ClientManifest
-  serializer?: Function
-  runInNewContext?: boolean | 'once'
-}
-
-export function createRenderer({
-  modules = [],
-  directives = {},
-  isUnaryTag = () => false,
-  template,
-  inject,
-  cache,
-  shouldPreload,
-  shouldPrefetch,
-  clientManifest,
-  serializer
-}: RenderOptions = {}): Renderer {
-  const render = createRenderFunction(modules, directives, isUnaryTag, cache)
-  const templateRenderer = new TemplateRenderer({
-    template,
-    inject,
-    // @ts-expect-error
-    shouldPreload,
-    // @ts-expect-error
-    shouldPrefetch,
-    clientManifest,
-    serializer
-  })
-
-  return {
-    renderToString(
-      component: Component,
-      context?: any,
-      cb?: any
-    ): Promise<string> {
-      if (typeof context === 'function') {
-        cb = context
-        context = {}
-      }
-      if (context) {
-        templateRenderer.bindRenderFns(context)
-      }
-
-      // no callback, return Promise
-      let promise
-      if (!cb) {
-        ;({ promise, cb } = createPromiseCallback())
-      }
-
-      let result = ''
-      const write = createWriteFunction(text => {
-        result += text
-        return false
-      }, cb)
-      try {
-        // @ts-expect-error TODO improve
-        render(component, write, context, err => {
-          if (err) {
-            return cb(err)
-          }
-          if (context && context.rendered) {
-            context.rendered(context)
-          }
-          if (template) {
-            try {
-              const res = templateRenderer.render(result, context)
-              if (typeof res !== 'string') {
-                // function template returning promise
-                res.then(html => cb(null, html)).catch(cb)
-              } else {
-                cb(null, res)
-              }
-            } catch (e: any) {
-              cb(e)
-            }
-          } else {
-            cb(null, result)
-          }
-        })
-      } catch (e: any) {
-        cb(e)
-      }
-
-      return promise
-    },
-
-    renderToStream(component: Component, context?: Object): Readable {
-      if (context) {
-        templateRenderer.bindRenderFns(context)
-      }
-      const renderStream = new RenderStream((write, done) => {
-        // @ts-expect-error
-        render(component, write, context, done)
-      })
-      if (!template) {
-        // @ts-expect-error
-        if (context && context.rendered) {
-          // @ts-expect-error
-          const rendered = context.rendered
-          renderStream.once('beforeEnd', () => {
-            rendered(context)
-          })
-        }
-        return renderStream
-      } else if (typeof template === 'function') {
-        throw new Error(
-          `function template is only supported in renderToString.`
-        )
-      } else {
-        const templateStream = templateRenderer.createStream(context)
-        renderStream.on('error', err => {
-          templateStream.emit('error', err)
-        })
-        renderStream.pipe(templateStream)
-        //@ts-expect-error
-        if (context && context.rendered) {
-          //@ts-expect-error
-          const rendered = context.rendered
-          renderStream.once('beforeEnd', () => {
-            rendered(context)
-          })
-        }
-        return templateStream
-      }
-    }
-  }
-}
diff --git a/packages/server-renderer/src/directives/index.ts b/packages/server-renderer/src/directives/index.ts
deleted file mode 100644
index fd7fce0a34c..00000000000
--- a/packages/server-renderer/src/directives/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import show from './show'
-import model from './model'
-
-export default {
-  show,
-  model
-}
diff --git a/packages/server-renderer/src/directives/model.ts b/packages/server-renderer/src/directives/model.ts
deleted file mode 100644
index fa54933e0af..00000000000
--- a/packages/server-renderer/src/directives/model.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { looseEqual, looseIndexOf } from 'shared/util'
-import type { VNodeDirective, VNodeWithData } from 'types/vnode'
-
-// this is only applied for <select v-model> because it is the only edge case
-// that must be done at runtime instead of compile time.
-export default function model(node: VNodeWithData, dir: VNodeDirective) {
-  if (!node.children) return
-  const value = dir.value
-  const isMultiple = node.data.attrs && node.data.attrs.multiple
-  for (let i = 0, l = node.children.length; i < l; i++) {
-    const option = node.children[i]
-    if (option.tag === 'option') {
-      if (isMultiple) {
-        const selected =
-          Array.isArray(value) && looseIndexOf(value, getValue(option)) > -1
-        if (selected) {
-          setSelected(option)
-        }
-      } else {
-        if (looseEqual(value, getValue(option))) {
-          setSelected(option)
-          return
-        }
-      }
-    }
-  }
-}
-
-function getValue(option) {
-  const data = option.data || {}
-  return (
-    (data.attrs && data.attrs.value) ||
-    (data.domProps && data.domProps.value) ||
-    (option.children && option.children[0] && option.children[0].text)
-  )
-}
-
-function setSelected(option) {
-  const data = option.data || (option.data = {})
-  const attrs = data.attrs || (data.attrs = {})
-  attrs.selected = ''
-}
diff --git a/packages/server-renderer/src/directives/show.ts b/packages/server-renderer/src/directives/show.ts
deleted file mode 100644
index 4a84fc6168c..00000000000
--- a/packages/server-renderer/src/directives/show.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { VNodeDirective, VNodeWithData } from 'types/vnode'
-
-export default function show(node: VNodeWithData, dir: VNodeDirective) {
-  if (!dir.value) {
-    const style: any = node.data.style || (node.data.style = {})
-    if (Array.isArray(style)) {
-      style.push({ display: 'none' })
-    } else {
-      style.display = 'none'
-    }
-  }
-}
diff --git a/packages/server-renderer/src/index-basic.ts b/packages/server-renderer/src/index-basic.ts
deleted file mode 100644
index ea8babbe0b9..00000000000
--- a/packages/server-renderer/src/index-basic.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import modules from './modules/index'
-import directives from './directives/index'
-import { isUnaryTag, canBeLeftOpenTag } from 'web/compiler/util'
-import { createBasicRenderer } from 'server/create-basic-renderer'
-
-export default createBasicRenderer({
-  // @ts-expect-error
-  modules,
-  directives,
-  isUnaryTag,
-  canBeLeftOpenTag
-})
diff --git a/packages/server-renderer/src/index.ts b/packages/server-renderer/src/index.ts
deleted file mode 100644
index 604c69938d0..00000000000
--- a/packages/server-renderer/src/index.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-process.env.VUE_ENV = 'server'
-
-import { extend } from 'shared/util'
-import modules from './modules/index'
-import baseDirectives from './directives/index'
-import { isUnaryTag, canBeLeftOpenTag } from 'web/compiler/util'
-
-import {
-  createRenderer as _createRenderer,
-  Renderer,
-  RenderOptions
-} from 'server/create-renderer'
-import { createBundleRendererCreator } from 'server/bundle-renderer/create-bundle-renderer'
-
-export function createRenderer(
-  options: RenderOptions | undefined = {}
-): Renderer {
-  return _createRenderer(
-    extend(extend({}, options), {
-      isUnaryTag,
-      canBeLeftOpenTag,
-      modules,
-      // user can provide server-side implementations for custom directives
-      // when creating the renderer.
-      directives: extend(baseDirectives, options.directives)
-    })
-  )
-}
-
-export const createBundleRenderer = createBundleRendererCreator(createRenderer)
diff --git a/packages/server-renderer/src/modules/attrs.ts b/packages/server-renderer/src/modules/attrs.ts
deleted file mode 100644
index 943e48526f7..00000000000
--- a/packages/server-renderer/src/modules/attrs.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { escape } from '../util'
-
-import { isDef, isUndef, extend } from 'shared/util'
-
-import {
-  isBooleanAttr,
-  isEnumeratedAttr,
-  isFalsyAttrValue,
-  convertEnumeratedValue
-} from 'web/util/attrs'
-
-import { isSSRUnsafeAttr } from '../util'
-import type { VNodeWithData } from 'types/vnode'
-
-export default function renderAttrs(node: VNodeWithData): string {
-  let attrs = node.data.attrs
-  let res = ''
-
-  const opts = node.parent && node.parent.componentOptions
-  if (isUndef(opts) || opts.Ctor.options.inheritAttrs !== false) {
-    let parent = node.parent
-    while (isDef(parent)) {
-      // Stop fallthrough in case parent has inheritAttrs option set to false
-      if (
-        parent.componentOptions &&
-        parent.componentOptions.Ctor.options.inheritAttrs === false
-      ) {
-        break
-      }
-      if (isDef(parent.data) && isDef(parent.data.attrs)) {
-        attrs = extend(extend({}, attrs), parent.data.attrs)
-      }
-      parent = parent.parent
-    }
-  }
-
-  if (isUndef(attrs)) {
-    return res
-  }
-
-  for (const key in attrs) {
-    if (isSSRUnsafeAttr(key)) {
-      continue
-    }
-    if (key === 'style') {
-      // leave it to the style module
-      continue
-    }
-    res += renderAttr(key, attrs[key])
-  }
-  return res
-}
-
-export function renderAttr(key: string, value: string): string {
-  if (isBooleanAttr(key)) {
-    if (!isFalsyAttrValue(value)) {
-      return ` ${key}="${key}"`
-    }
-  } else if (isEnumeratedAttr(key)) {
-    return ` ${key}="${escape(convertEnumeratedValue(key, value))}"`
-  } else if (!isFalsyAttrValue(value)) {
-    return ` ${key}="${escape(String(value))}"`
-  }
-  return ''
-}
diff --git a/packages/server-renderer/src/modules/class.ts b/packages/server-renderer/src/modules/class.ts
deleted file mode 100644
index 5fe93fb304a..00000000000
--- a/packages/server-renderer/src/modules/class.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { escape } from '../util'
-import { genClassForVnode } from 'web/util/index'
-import type { VNodeWithData } from 'types/vnode'
-
-export default function renderClass(node: VNodeWithData): string | undefined {
-  const classList = genClassForVnode(node)
-  if (classList !== '') {
-    return ` class="${escape(classList)}"`
-  }
-}
diff --git a/packages/server-renderer/src/modules/dom-props.ts b/packages/server-renderer/src/modules/dom-props.ts
deleted file mode 100644
index 3c4d12625f3..00000000000
--- a/packages/server-renderer/src/modules/dom-props.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import VNode from 'core/vdom/vnode'
-import { renderAttr } from './attrs'
-import { isDef, isUndef, extend, toString } from 'shared/util'
-import { propsToAttrMap, isRenderableAttr } from '../util'
-import type { VNodeWithData } from 'types/vnode'
-
-export default function renderDOMProps(node: VNodeWithData): string {
-  let props = node.data.domProps
-  let res = ''
-
-  let parent = node.parent
-  while (isDef(parent)) {
-    if (parent.data && parent.data.domProps) {
-      props = extend(extend({}, props), parent.data.domProps)
-    }
-    parent = parent.parent
-  }
-
-  if (isUndef(props)) {
-    return res
-  }
-
-  const attrs = node.data.attrs
-  for (const key in props) {
-    if (key === 'innerHTML') {
-      setText(node, props[key], true)
-    } else if (key === 'textContent') {
-      setText(node, props[key], false)
-    } else if (key === 'value' && node.tag === 'textarea') {
-      setText(node, toString(props[key]), false)
-    } else {
-      // $flow-disable-line (WTF?)
-      const attr = propsToAttrMap[key] || key.toLowerCase()
-      if (
-        isRenderableAttr(attr) &&
-        // avoid rendering double-bound props/attrs twice
-        !(isDef(attrs) && isDef(attrs[attr]))
-      ) {
-        res += renderAttr(attr, props[key])
-      }
-    }
-  }
-  return res
-}
-
-function setText(node, text, raw) {
-  const child = new VNode(undefined, undefined, undefined, text)
-  child.raw = raw
-  node.children = [child]
-}
diff --git a/packages/server-renderer/src/modules/index.ts b/packages/server-renderer/src/modules/index.ts
deleted file mode 100644
index 40dd926ff19..00000000000
--- a/packages/server-renderer/src/modules/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import attrs from './attrs'
-import domProps from './dom-props'
-import klass from './class'
-import style from './style'
-
-export default [attrs, domProps, klass, style]
diff --git a/packages/server-renderer/src/modules/style.ts b/packages/server-renderer/src/modules/style.ts
deleted file mode 100644
index 23c8e0d4237..00000000000
--- a/packages/server-renderer/src/modules/style.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { escape, noUnitNumericStyleProps } from '../util'
-import { hyphenate } from 'shared/util'
-import { getStyle } from 'web/util/style'
-import type { VNodeWithData } from 'types/vnode'
-
-export function genStyle(style: Object): string {
-  let styleText = ''
-  for (const key in style) {
-    const value = style[key]
-    const hyphenatedKey = hyphenate(key)
-    if (Array.isArray(value)) {
-      for (let i = 0, len = value.length; i < len; i++) {
-        styleText += normalizeValue(hyphenatedKey, value[i])
-      }
-    } else {
-      styleText += normalizeValue(hyphenatedKey, value)
-    }
-  }
-  return styleText
-}
-
-function normalizeValue(key: string, value: any): string {
-  if (
-    typeof value === 'string' ||
-    (typeof value === 'number' && noUnitNumericStyleProps[key]) ||
-    value === 0
-  ) {
-    return `${key}:${value};`
-  } else {
-    // invalid values
-    return ``
-  }
-}
-
-export default function renderStyle(vnode: VNodeWithData): string | undefined {
-  const styleText = genStyle(getStyle(vnode, false))
-  if (styleText !== '') {
-    return ` style=${JSON.stringify(escape(styleText))}`
-  }
-}
diff --git a/packages/server-renderer/src/optimizing-compiler/codegen.ts b/packages/server-renderer/src/optimizing-compiler/codegen.ts
deleted file mode 100644
index 36669e191d2..00000000000
--- a/packages/server-renderer/src/optimizing-compiler/codegen.ts
+++ /dev/null
@@ -1,260 +0,0 @@
-// The SSR codegen is essentially extending the default codegen to handle
-// SSR-optimizable nodes and turn them into string render fns. In cases where
-// a node is not optimizable it simply falls back to the default codegen.
-
-import {
-  genIf,
-  genFor,
-  genData,
-  genText,
-  genElement,
-  genChildren,
-  CodegenState
-} from 'compiler/codegen/index'
-
-import {
-  genAttrSegments,
-  genDOMPropSegments,
-  genClassSegments,
-  genStyleSegments,
-  applyModelTransform
-} from './modules'
-
-import { escape } from '../util'
-import { optimizability } from './optimizer'
-import type { CodegenResult } from 'compiler/codegen/index'
-import { ASTElement, ASTNode, CompilerOptions } from 'types/compiler'
-
-export type StringSegment = {
-  type: number
-  value: string
-}
-
-// segment types
-export const RAW = 0
-export const INTERPOLATION = 1
-export const EXPRESSION = 2
-
-export function generate(
-  ast: ASTElement | void,
-  options: CompilerOptions
-): CodegenResult {
-  const state = new CodegenState(options)
-  const code = ast ? genSSRElement(ast, state) : '_c("div")'
-  return {
-    render: `with(this){return ${code}}`,
-    staticRenderFns: state.staticRenderFns
-  }
-}
-
-function genSSRElement(el: ASTElement, state: CodegenState): string {
-  if (el.for && !el.forProcessed) {
-    return genFor(el, state, genSSRElement)
-  } else if (el.if && !el.ifProcessed) {
-    return genIf(el, state, genSSRElement)
-  } else if (el.tag === 'template' && !el.slotTarget) {
-    return el.ssrOptimizability === optimizability.FULL
-      ? genChildrenAsStringNode(el, state)
-      : genSSRChildren(el, state) || 'void 0'
-  }
-
-  switch (el.ssrOptimizability) {
-    case optimizability.FULL:
-      // stringify whole tree
-      return genStringElement(el, state)
-    case optimizability.SELF:
-      // stringify self and check children
-      return genStringElementWithChildren(el, state)
-    case optimizability.CHILDREN:
-      // generate self as VNode and stringify children
-      return genNormalElement(el, state, true)
-    case optimizability.PARTIAL:
-      // generate self as VNode and check children
-      return genNormalElement(el, state, false)
-    default:
-      // bail whole tree
-      return genElement(el, state)
-  }
-}
-
-function genNormalElement(el, state, stringifyChildren) {
-  const data = el.plain ? undefined : genData(el, state)
-  const children = stringifyChildren
-    ? `[${genChildrenAsStringNode(el, state)}]`
-    : genSSRChildren(el, state, true)
-  return `_c('${el.tag}'${data ? `,${data}` : ''}${
-    children ? `,${children}` : ''
-  })`
-}
-
-function genSSRChildren(el, state, checkSkip?: boolean) {
-  return genChildren(el, state, checkSkip, genSSRElement, genSSRNode)
-}
-
-function genSSRNode(el, state) {
-  return el.type === 1 ? genSSRElement(el, state) : genText(el)
-}
-
-function genChildrenAsStringNode(el, state) {
-  return el.children.length
-    ? `_ssrNode(${flattenSegments(childrenToSegments(el, state))})`
-    : ''
-}
-
-function genStringElement(el, state) {
-  return `_ssrNode(${elementToString(el, state)})`
-}
-
-function genStringElementWithChildren(el, state) {
-  const children = genSSRChildren(el, state, true)
-  return `_ssrNode(${flattenSegments(elementToOpenTagSegments(el, state))},"</${
-    el.tag
-  }>"${children ? `,${children}` : ''})`
-}
-
-function elementToString(el, state) {
-  return `(${flattenSegments(elementToSegments(el, state))})`
-}
-
-function elementToSegments(el, state): Array<StringSegment> {
-  // v-for / v-if
-  if (el.for && !el.forProcessed) {
-    el.forProcessed = true
-    return [
-      {
-        type: EXPRESSION,
-        value: genFor(el, state, elementToString, '_ssrList')
-      }
-    ]
-  } else if (el.if && !el.ifProcessed) {
-    el.ifProcessed = true
-    return [
-      {
-        type: EXPRESSION,
-        value: genIf(el, state, elementToString, '"<!---->"')
-      }
-    ]
-  } else if (el.tag === 'template') {
-    return childrenToSegments(el, state)
-  }
-
-  const openSegments = elementToOpenTagSegments(el, state)
-  const childrenSegments = childrenToSegments(el, state)
-  const { isUnaryTag } = state.options
-  const close =
-    isUnaryTag && isUnaryTag(el.tag)
-      ? []
-      : [{ type: RAW, value: `</${el.tag}>` }]
-  return openSegments.concat(childrenSegments, close)
-}
-
-function elementToOpenTagSegments(el, state): Array<StringSegment> {
-  applyModelTransform(el, state)
-  let binding
-  const segments = [{ type: RAW, value: `<${el.tag}` }]
-  // attrs
-  if (el.attrs) {
-    segments.push.apply(segments, genAttrSegments(el.attrs))
-  }
-  // domProps
-  if (el.props) {
-    segments.push.apply(segments, genDOMPropSegments(el.props, el.attrs))
-  }
-  // v-bind="object"
-  if ((binding = el.attrsMap['v-bind'])) {
-    segments.push({ type: EXPRESSION, value: `_ssrAttrs(${binding})` })
-  }
-  // v-bind.prop="object"
-  if ((binding = el.attrsMap['v-bind.prop'])) {
-    segments.push({ type: EXPRESSION, value: `_ssrDOMProps(${binding})` })
-  }
-  // class
-  if (el.staticClass || el.classBinding) {
-    segments.push.apply(
-      segments,
-      genClassSegments(el.staticClass, el.classBinding)
-    )
-  }
-  // style & v-show
-  if (el.staticStyle || el.styleBinding || el.attrsMap['v-show']) {
-    segments.push.apply(
-      segments,
-      genStyleSegments(
-        el.attrsMap.style,
-        el.staticStyle,
-        el.styleBinding,
-        el.attrsMap['v-show']
-      )
-    )
-  }
-  // _scopedId
-  if (state.options.scopeId) {
-    segments.push({ type: RAW, value: ` ${state.options.scopeId}` })
-  }
-  segments.push({ type: RAW, value: `>` })
-  return segments
-}
-
-function childrenToSegments(el, state): Array<StringSegment> {
-  let binding
-  if ((binding = el.attrsMap['v-html'])) {
-    return [{ type: EXPRESSION, value: `_s(${binding})` }]
-  }
-  if ((binding = el.attrsMap['v-text'])) {
-    return [{ type: INTERPOLATION, value: `_s(${binding})` }]
-  }
-  if (el.tag === 'textarea' && (binding = el.attrsMap['v-model'])) {
-    return [{ type: INTERPOLATION, value: `_s(${binding})` }]
-  }
-  return el.children ? nodesToSegments(el.children, state) : []
-}
-
-function nodesToSegments(
-  children: Array<ASTNode>,
-  state: CodegenState
-): Array<StringSegment> {
-  const segments: StringSegment[] = []
-  for (let i = 0; i < children.length; i++) {
-    const c = children[i]
-    if (c.type === 1) {
-      segments.push.apply(segments, elementToSegments(c, state))
-    } else if (c.type === 2) {
-      segments.push({ type: INTERPOLATION, value: c.expression })
-    } else if (c.type === 3) {
-      let text = escape(c.text)
-      if (c.isComment) {
-        text = '<!--' + text + '-->'
-      }
-      segments.push({ type: RAW, value: text })
-    }
-  }
-  return segments
-}
-
-function flattenSegments(segments: Array<StringSegment>): string {
-  const mergedSegments: string[] = []
-  let textBuffer = ''
-
-  const pushBuffer = () => {
-    if (textBuffer) {
-      mergedSegments.push(JSON.stringify(textBuffer))
-      textBuffer = ''
-    }
-  }
-
-  for (let i = 0; i < segments.length; i++) {
-    const s = segments[i]
-    if (s.type === RAW) {
-      textBuffer += s.value
-    } else if (s.type === INTERPOLATION) {
-      pushBuffer()
-      mergedSegments.push(`_ssrEscape(${s.value})`)
-    } else if (s.type === EXPRESSION) {
-      pushBuffer()
-      mergedSegments.push(`(${s.value})`)
-    }
-  }
-  pushBuffer()
-
-  return mergedSegments.join('+')
-}
diff --git a/packages/server-renderer/src/optimizing-compiler/index.ts b/packages/server-renderer/src/optimizing-compiler/index.ts
deleted file mode 100644
index 780d8277806..00000000000
--- a/packages/server-renderer/src/optimizing-compiler/index.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { parse } from 'compiler/parser/index'
-import { generate } from './codegen'
-import { optimize } from './optimizer'
-import { createCompilerCreator } from 'compiler/create-compiler'
-import { CompiledResult, CompilerOptions } from 'types/compiler'
-
-export const createCompiler = createCompilerCreator(function baseCompile(
-  template: string,
-  options: CompilerOptions
-): CompiledResult {
-  const ast = parse(template.trim(), options)
-  optimize(ast, options)
-  const code = generate(ast, options)
-  return {
-    ast,
-    render: code.render,
-    staticRenderFns: code.staticRenderFns
-  }
-})
diff --git a/packages/server-renderer/src/optimizing-compiler/modules.ts b/packages/server-renderer/src/optimizing-compiler/modules.ts
deleted file mode 100644
index 3ece915ead4..00000000000
--- a/packages/server-renderer/src/optimizing-compiler/modules.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-import {
-  RAW,
-  // INTERPOLATION,
-  EXPRESSION
-} from './codegen'
-
-import { propsToAttrMap, isRenderableAttr } from '../util'
-
-import { isBooleanAttr, isEnumeratedAttr } from 'web/util/attrs'
-
-import type { StringSegment } from './codegen'
-import type { CodegenState } from 'compiler/codegen/index'
-import { ASTAttr, ASTElement } from 'types/compiler'
-
-const plainStringRE = /^"(?:[^"\\]|\\.)*"$|^'(?:[^'\\]|\\.)*'$/
-
-// let the model AST transform translate v-model into appropriate
-// props bindings
-export function applyModelTransform(el: ASTElement, state: CodegenState) {
-  if (el.directives) {
-    for (let i = 0; i < el.directives.length; i++) {
-      const dir = el.directives[i]
-      if (dir.name === 'model') {
-        state.directives.model(el, dir, state.warn)
-        // remove value for textarea as its converted to text
-        if (el.tag === 'textarea' && el.props) {
-          el.props = el.props.filter(p => p.name !== 'value')
-        }
-        break
-      }
-    }
-  }
-}
-
-export function genAttrSegments(attrs: Array<ASTAttr>): Array<StringSegment> {
-  return attrs.map(({ name, value }) => genAttrSegment(name, value))
-}
-
-export function genDOMPropSegments(
-  props: Array<ASTAttr>,
-  attrs: Array<ASTAttr> | null | undefined
-): Array<StringSegment> {
-  const segments: StringSegment[] = []
-  props.forEach(({ name, value }) => {
-    name = propsToAttrMap[name] || name.toLowerCase()
-    if (
-      isRenderableAttr(name) &&
-      !(attrs && attrs.some(a => a.name === name))
-    ) {
-      segments.push(genAttrSegment(name, value))
-    }
-  })
-  return segments
-}
-
-function genAttrSegment(name: string, value: string): StringSegment {
-  if (plainStringRE.test(value)) {
-    // force double quote
-    value = value.replace(/^'|'$/g, '"')
-    // force enumerated attr to "true"
-    if (isEnumeratedAttr(name) && value !== `"false"`) {
-      value = `"true"`
-    }
-    return {
-      type: RAW,
-      value: isBooleanAttr(name)
-        ? ` ${name}="${name}"`
-        : value === '""'
-        ? ` ${name}`
-        : ` ${name}="${JSON.parse(value)}"`
-    }
-  } else {
-    return {
-      type: EXPRESSION,
-      value: `_ssrAttr(${JSON.stringify(name)},${value})`
-    }
-  }
-}
-
-export function genClassSegments(
-  staticClass: string | null | undefined,
-  classBinding: string | null | undefined
-): Array<StringSegment> {
-  if (staticClass && !classBinding) {
-    return [{ type: RAW, value: ` class="${JSON.parse(staticClass)}"` }]
-  } else {
-    return [
-      {
-        type: EXPRESSION,
-        value: `_ssrClass(${staticClass || 'null'},${classBinding || 'null'})`
-      }
-    ]
-  }
-}
-
-export function genStyleSegments(
-  staticStyle: string | null | undefined,
-  parsedStaticStyle: string | null | undefined,
-  styleBinding: string | null | undefined,
-  vShowExpression: string | null | undefined
-): Array<StringSegment> {
-  if (staticStyle && !styleBinding && !vShowExpression) {
-    return [{ type: RAW, value: ` style=${JSON.stringify(staticStyle)}` }]
-  } else {
-    return [
-      {
-        type: EXPRESSION,
-        value: `_ssrStyle(${parsedStaticStyle || 'null'},${
-          styleBinding || 'null'
-        }, ${
-          vShowExpression
-            ? `{ display: (${vShowExpression}) ? '' : 'none' }`
-            : 'null'
-        })`
-      }
-    ]
-  }
-}
diff --git a/packages/server-renderer/src/optimizing-compiler/optimizer.ts b/packages/server-renderer/src/optimizing-compiler/optimizer.ts
deleted file mode 100644
index dfb9fb52388..00000000000
--- a/packages/server-renderer/src/optimizing-compiler/optimizer.ts
+++ /dev/null
@@ -1,140 +0,0 @@
-/**
- * In SSR, the vdom tree is generated only once and never patched, so
- * we can optimize most element / trees into plain string render functions.
- * The SSR optimizer walks the AST tree to detect optimizable elements and trees.
- *
- * The criteria for SSR optimizability is quite a bit looser than static tree
- * detection (which is designed for client re-render). In SSR we bail only for
- * components/slots/custom directives.
- */
-
-import { no, makeMap, isBuiltInTag } from 'shared/util'
-import { ASTElement, ASTNode, CompilerOptions } from 'types/compiler'
-
-// optimizability constants
-export const optimizability = {
-  FALSE: 0, // whole sub tree un-optimizable
-  FULL: 1, // whole sub tree optimizable
-  SELF: 2, // self optimizable but has some un-optimizable children
-  CHILDREN: 3, // self un-optimizable but have fully optimizable children
-  PARTIAL: 4 // self un-optimizable with some un-optimizable children
-}
-
-let isPlatformReservedTag
-
-export function optimize(root: ASTElement | null, options: CompilerOptions) {
-  if (!root) return
-  isPlatformReservedTag = options.isReservedTag || no
-  walk(root, true)
-}
-
-function walk(node: ASTNode, isRoot?: boolean) {
-  if (isUnOptimizableTree(node)) {
-    node.ssrOptimizability = optimizability.FALSE
-    return
-  }
-  // root node or nodes with custom directives should always be a VNode
-  const selfUnoptimizable = isRoot || hasCustomDirective(node)
-  const check = child => {
-    if (child.ssrOptimizability !== optimizability.FULL) {
-      node.ssrOptimizability = selfUnoptimizable
-        ? optimizability.PARTIAL
-        : optimizability.SELF
-    }
-  }
-  if (selfUnoptimizable) {
-    node.ssrOptimizability = optimizability.CHILDREN
-  }
-  if (node.type === 1) {
-    for (let i = 0, l = node.children.length; i < l; i++) {
-      const child = node.children[i]
-      walk(child)
-      check(child)
-    }
-    if (node.ifConditions) {
-      for (let i = 1, l = node.ifConditions.length; i < l; i++) {
-        const block = node.ifConditions[i].block
-        walk(block, isRoot)
-        check(block)
-      }
-    }
-    if (
-      node.ssrOptimizability == null ||
-      (!isRoot && (node.attrsMap['v-html'] || node.attrsMap['v-text']))
-    ) {
-      node.ssrOptimizability = optimizability.FULL
-    } else {
-      node.children = optimizeSiblings(node)
-    }
-  } else {
-    node.ssrOptimizability = optimizability.FULL
-  }
-}
-
-function optimizeSiblings(el) {
-  const children = el.children
-  const optimizedChildren: any[] = []
-
-  let currentOptimizableGroup: any[] = []
-  const pushGroup = () => {
-    if (currentOptimizableGroup.length) {
-      optimizedChildren.push({
-        type: 1,
-        parent: el,
-        tag: 'template',
-        attrsList: [],
-        attrsMap: {},
-        rawAttrsMap: {},
-        children: currentOptimizableGroup,
-        ssrOptimizability: optimizability.FULL
-      })
-    }
-    currentOptimizableGroup = []
-  }
-
-  for (let i = 0; i < children.length; i++) {
-    const c = children[i]
-    if (c.ssrOptimizability === optimizability.FULL) {
-      currentOptimizableGroup.push(c)
-    } else {
-      // wrap fully-optimizable adjacent siblings inside a template tag
-      // so that they can be optimized into a single ssrNode by codegen
-      pushGroup()
-      optimizedChildren.push(c)
-    }
-  }
-  pushGroup()
-  return optimizedChildren
-}
-
-function isUnOptimizableTree(node: ASTNode): boolean {
-  if (node.type === 2 || node.type === 3) {
-    // text or expression
-    return false
-  }
-  return (
-    isBuiltInTag(node.tag) || // built-in (slot, component)
-    !isPlatformReservedTag(node.tag) || // custom component
-    !!node.component || // "is" component
-    isSelectWithModel(node) // <select v-model> requires runtime inspection
-  )
-}
-
-const isBuiltInDir = makeMap('text,html,show,on,bind,model,pre,cloak,once')
-
-function hasCustomDirective(node: ASTNode): boolean {
-  return (node.type === 1 &&
-    node.directives &&
-    node.directives.some(d => !isBuiltInDir(d.name))) as any
-}
-
-// <select v-model> cannot be optimized because it requires a runtime check
-// to determine proper selected option
-function isSelectWithModel(node: ASTNode): boolean {
-  return (
-    node.type === 1 &&
-    node.tag === 'select' &&
-    node.directives != null &&
-    node.directives.some(d => d.name === 'model')
-  )
-}
diff --git a/packages/server-renderer/src/optimizing-compiler/runtime-helpers.ts b/packages/server-renderer/src/optimizing-compiler/runtime-helpers.ts
deleted file mode 100644
index 7809d4e0a31..00000000000
--- a/packages/server-renderer/src/optimizing-compiler/runtime-helpers.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-import {
-  escape,
-  isSSRUnsafeAttr,
-  propsToAttrMap,
-  isRenderableAttr
-} from '../util'
-import { isObject, extend } from 'shared/util'
-import { renderAttr } from '../modules/attrs'
-import { renderClass } from 'web/util/class'
-import { genStyle } from '../modules/style'
-import { normalizeStyleBinding } from 'web/util/style'
-
-import {
-  normalizeChildren,
-  simpleNormalizeChildren
-} from 'core/vdom/helpers/normalize-children'
-
-import type { Component } from 'types/component'
-
-const ssrHelpers = {
-  _ssrEscape: escape,
-  _ssrNode: renderStringNode,
-  _ssrList: renderStringList,
-  _ssrAttr: renderAttr,
-  _ssrAttrs: renderAttrs,
-  _ssrDOMProps: renderDOMProps,
-  _ssrClass: renderSSRClass,
-  _ssrStyle: renderSSRStyle
-}
-
-export function installSSRHelpers(vm: Component) {
-  if (vm._ssrNode) {
-    return
-  }
-  let Vue = vm.constructor
-  // @ts-expect-error
-  while (Vue.super) {
-    // @ts-expect-error
-    Vue = Vue.super
-  }
-  extend(Vue.prototype, ssrHelpers)
-  // @ts-expect-error
-  if (Vue.FunctionalRenderContext) {
-    // @ts-expect-error
-    extend(Vue.FunctionalRenderContext.prototype, ssrHelpers)
-  }
-}
-
-class StringNode {
-  isString: boolean
-  open: string
-  close: string | undefined
-  children: Array<any> | undefined
-
-  constructor(
-    open: string,
-    close?: string,
-    children?: Array<any>,
-    normalizationType?: number
-  ) {
-    this.isString = true
-    this.open = open
-    this.close = close
-    if (children) {
-      this.children =
-        normalizationType === 1
-          ? simpleNormalizeChildren(children)
-          : normalizationType === 2
-          ? normalizeChildren(children)
-          : children
-    } else {
-      this.children = void 0
-    }
-  }
-}
-
-function renderStringNode(
-  open: string,
-  close?: string,
-  children?: Array<any>,
-  normalizationType?: number
-): StringNode {
-  return new StringNode(open, close, children, normalizationType)
-}
-
-function renderStringList(
-  val: any,
-  render: (val: any, keyOrIndex: string | number, index?: number) => string
-): string {
-  let ret = ''
-  let i, l, keys, key
-  if (Array.isArray(val) || typeof val === 'string') {
-    for (i = 0, l = val.length; i < l; i++) {
-      ret += render(val[i], i)
-    }
-  } else if (typeof val === 'number') {
-    for (i = 0; i < val; i++) {
-      ret += render(i + 1, i)
-    }
-  } else if (isObject(val)) {
-    keys = Object.keys(val)
-    for (i = 0, l = keys.length; i < l; i++) {
-      key = keys[i]
-      ret += render(val[key], key, i)
-    }
-  }
-  return ret
-}
-
-function renderAttrs(obj: Object): string {
-  let res = ''
-  for (const key in obj) {
-    if (isSSRUnsafeAttr(key)) {
-      continue
-    }
-    res += renderAttr(key, obj[key])
-  }
-  return res
-}
-
-function renderDOMProps(obj: Object): string {
-  let res = ''
-  for (const key in obj) {
-    const attr = propsToAttrMap[key] || key.toLowerCase()
-    if (isRenderableAttr(attr)) {
-      res += renderAttr(attr, obj[key])
-    }
-  }
-  return res
-}
-
-function renderSSRClass(staticClass: string | null, dynamic: any): string {
-  const res = renderClass(staticClass, dynamic)
-  return res === '' ? res : ` class="${escape(res)}"`
-}
-
-function renderSSRStyle(
-  staticStyle: Record<string, any>,
-  dynamic: any,
-  extra?: Record<string, any>
-): string {
-  const style = {}
-  if (staticStyle) extend(style, staticStyle)
-  if (dynamic) extend(style, normalizeStyleBinding(dynamic))
-  if (extra) extend(style, extra)
-  const res = genStyle(style)
-  return res === '' ? res : ` style=${JSON.stringify(escape(res))}`
-}
diff --git a/packages/server-renderer/src/render-context.ts b/packages/server-renderer/src/render-context.ts
deleted file mode 100644
index cd35727f412..00000000000
--- a/packages/server-renderer/src/render-context.ts
+++ /dev/null
@@ -1,134 +0,0 @@
-import VNode from 'core/vdom/vnode'
-import { isUndef } from 'shared/util'
-import { Component } from 'types/component'
-
-type RenderState =
-  | {
-      type: 'Element'
-      rendered: number
-      total: number
-      children: Array<VNode>
-      endTag: string
-    }
-  | {
-      type: 'Fragment'
-      rendered: number
-      total: number
-      children: Array<VNode>
-    }
-  | {
-      type: 'Component'
-      prevActive: Component
-    }
-  | {
-      type: 'ComponentWithCache'
-      buffer: Array<string>
-      bufferIndex: number
-      componentBuffer: Array<Set<Component>>
-      key: string
-    }
-
-export class RenderContext {
-  userContext: Record<string, any> | null
-  activeInstance: Component
-  renderStates: Array<RenderState>
-  write: (text: string, next: Function) => void
-  renderNode: (node: VNode, isRoot: boolean, context: RenderContext) => void
-  done: (err?: Error) => void
-
-  modules: Array<(node: VNode) => string | null>
-  directives: Object
-  isUnaryTag: (tag: string) => boolean
-
-  cache: any
-  get?: (key: string, cb: Function) => void
-  has?: (key: string, cb: Function) => void
-
-  constructor(options: Record<string, any>) {
-    this.userContext = options.userContext
-    this.activeInstance = options.activeInstance
-    this.renderStates = []
-
-    this.write = options.write
-    this.done = options.done
-    this.renderNode = options.renderNode
-
-    this.isUnaryTag = options.isUnaryTag
-    this.modules = options.modules
-    this.directives = options.directives
-
-    const cache = options.cache
-    if (cache && (!cache.get || !cache.set)) {
-      throw new Error('renderer cache must implement at least get & set.')
-    }
-    this.cache = cache
-    this.get = cache && normalizeAsync(cache, 'get')
-    this.has = cache && normalizeAsync(cache, 'has')
-
-    this.next = this.next.bind(this)
-  }
-
-  next() {
-    // eslint-disable-next-line
-    while (true) {
-      const lastState = this.renderStates[this.renderStates.length - 1]
-      if (isUndef(lastState)) {
-        return this.done()
-      }
-      /* eslint-disable no-case-declarations */
-      switch (lastState.type) {
-        case 'Element':
-        case 'Fragment':
-          const { children, total } = lastState
-          const rendered = lastState.rendered++
-          if (rendered < total) {
-            return this.renderNode(children[rendered], false, this)
-          } else {
-            this.renderStates.pop()
-            if (lastState.type === 'Element') {
-              return this.write(lastState.endTag, this.next)
-            }
-          }
-          break
-        case 'Component':
-          this.renderStates.pop()
-          this.activeInstance = lastState.prevActive
-          break
-        case 'ComponentWithCache':
-          this.renderStates.pop()
-          const { buffer, bufferIndex, componentBuffer, key } = lastState
-          const result = {
-            html: buffer[bufferIndex],
-            components: componentBuffer[bufferIndex]
-          }
-          this.cache.set(key, result)
-          if (bufferIndex === 0) {
-            // this is a top-level cached component,
-            // exit caching mode.
-            //@ts-expect-error
-            this.write.caching = false
-          } else {
-            // parent component is also being cached,
-            // merge self into parent's result
-            buffer[bufferIndex - 1] += result.html
-            const prev = componentBuffer[bufferIndex - 1]
-            result.components.forEach(c => prev.add(c))
-          }
-          buffer.length = bufferIndex
-          componentBuffer.length = bufferIndex
-          break
-      }
-    }
-  }
-}
-
-function normalizeAsync(cache, method) {
-  const fn = cache[method]
-  if (isUndef(fn)) {
-    return
-  } else if (fn.length > 1) {
-    return (key, cb) => fn.call(cache, key, cb)
-  } else {
-    return (key, cb) => cb(fn.call(cache, key))
-  }
-}
diff --git a/packages/server-renderer/src/render-stream.ts b/packages/server-renderer/src/render-stream.ts
deleted file mode 100644
index 6cbe6298cca..00000000000
--- a/packages/server-renderer/src/render-stream.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * Original RenderStream implementation by Sasha Aickin (@aickin)
- * Licensed under the Apache License, Version 2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Modified by Evan You (@yyx990803)
- */
-
-// const stream = require('stream')
-import { Readable } from 'stream'
-
-import { isTrue, isUndef } from 'shared/util'
-import { createWriteFunction } from './write'
-
-export default class RenderStream extends Readable {
-  buffer: string
-  render: (write: Function, done: Function) => void
-  expectedSize: number
-  write: Function
-  //@ts-expect-error
-  next: Function
-  end: Function
-  //@ts-expect-error
-  done: boolean
-
-  constructor(render: Function) {
-    super()
-    this.buffer = ''
-    //@ts-expect-error
-    this.render = render
-    this.expectedSize = 0
-
-    this.write = createWriteFunction(
-      (text, next) => {
-        const n = this.expectedSize
-        this.buffer += text
-        if (this.buffer.length >= n) {
-          this.next = next
-          this.pushBySize(n)
-          return true // we will decide when to call next
-        }
-        return false
-      },
-      err => {
-        this.emit('error', err)
-      }
-    )
-
-    this.end = () => {
-      this.emit('beforeEnd')
-      // the rendering is finished; we should push out the last of the buffer.
-      this.done = true
-      this.push(this.buffer)
-    }
-  }
-
-  pushBySize(n: number) {
-    const bufferToPush = this.buffer.substring(0, n)
-    this.buffer = this.buffer.substring(n)
-    this.push(bufferToPush)
-  }
-
-  tryRender() {
-    try {
-      this.render(this.write, this.end)
-    } catch (e) {
-      this.emit('error', e)
-    }
-  }
-
-  tryNext() {
-    try {
-      this.next()
-    } catch (e) {
-      this.emit('error', e)
-    }
-  }
-
-  _read(n: number) {
-    this.expectedSize = n
-    // it's possible that the last chunk added bumped the buffer up to > 2 * n,
-    // which means we will need to go through multiple read calls to drain it
-    // down to < n.
-    if (isTrue(this.done)) {
-      this.push(null)
-      return
-    }
-    if (this.buffer.length >= n) {
-      this.pushBySize(n)
-      return
-    }
-    if (isUndef(this.next)) {
-      // start the rendering chain.
-      this.tryRender()
-    } else {
-      // continue with the rendering.
-      this.tryNext()
-    }
-  }
-}
diff --git a/packages/server-renderer/src/render.ts b/packages/server-renderer/src/render.ts
deleted file mode 100644
index b1116840ed5..00000000000
--- a/packages/server-renderer/src/render.ts
+++ /dev/null
@@ -1,459 +0,0 @@
-import { escape } from './util'
-import { SSR_ATTR } from 'shared/constants'
-import { RenderContext } from './render-context'
-import { resolveAsset } from 'core/util/options'
-import { generateComponentTrace } from 'core/util/debug'
-import { ssrCompileToFunctions } from './compiler'
-import { installSSRHelpers } from './optimizing-compiler/runtime-helpers'
-
-import { isDef, isUndef, isTrue } from 'shared/util'
-
-import {
-  createComponent,
-  createComponentInstanceForVnode
-} from 'core/vdom/create-component'
-import VNode from 'core/vdom/vnode'
-import type { VNodeDirective } from 'types/vnode'
-import type { Component } from 'types/component'
-
-let warned = Object.create(null)
-const warnOnce = msg => {
-  if (!warned[msg]) {
-    warned[msg] = true
-    // eslint-disable-next-line no-console
-    console.warn(`\n\u001b[31m${msg}\u001b[39m\n`)
-  }
-}
-
-const onCompilationError = (err, vm) => {
-  const trace = vm ? generateComponentTrace(vm) : ''
-  throw new Error(`\n\u001b[31m${err}${trace}\u001b[39m\n`)
-}
-
-const normalizeRender = vm => {
-  const { render, template, _scopeId } = vm.$options
-  if (isUndef(render)) {
-    if (template) {
-      const compiled = ssrCompileToFunctions(
-        template,
-        {
-          scopeId: _scopeId,
-          warn: onCompilationError
-        },
-        vm
-      )
-
-      vm.$options.render = compiled.render
-      vm.$options.staticRenderFns = compiled.staticRenderFns
-    } else {
-      throw new Error(
-        `render function or template not defined in component: ${
-          vm.$options.name || vm.$options._componentTag || 'anonymous'
-        }`
-      )
-    }
-  }
-}
-
-function waitForServerPrefetch(vm, resolve, reject) {
-  let handlers = vm.$options.serverPrefetch
-  if (isDef(handlers)) {
-    if (!Array.isArray(handlers)) handlers = [handlers]
-    try {
-      const promises: Promise<any>[] = []
-      for (let i = 0, j = handlers.length; i < j; i++) {
-        const result = handlers[i].call(vm, vm)
-        if (result && typeof result.then === 'function') {
-          promises.push(result)
-        }
-      }
-      Promise.all(promises).then(resolve).catch(reject)
-      return
-    } catch (e: any) {
-      reject(e)
-    }
-  }
-  resolve()
-}
-
-function renderNode(node, isRoot, context) {
-  if (node.isString) {
-    renderStringNode(node, context)
-  } else if (isDef(node.componentOptions)) {
-    renderComponent(node, isRoot, context)
-  } else if (isDef(node.tag)) {
-    renderElement(node, isRoot, context)
-  } else if (isTrue(node.isComment)) {
-    if (isDef(node.asyncFactory)) {
-      // async component
-      renderAsyncComponent(node, isRoot, context)
-    } else {
-      context.write(`<!--${node.text}-->`, context.next)
-    }
-  } else {
-    context.write(
-      node.raw ? node.text : escape(String(node.text)),
-      context.next
-    )
-  }
-}
-
-function registerComponentForCache(options, write) {
-  // exposed by vue-loader, need to call this if cache hit because
-  // component lifecycle hooks will not be called.
-  const register = options._ssrRegister
-  if (write.caching && isDef(register)) {
-    write.componentBuffer[write.componentBuffer.length - 1].add(register)
-  }
-  return register
-}
-
-function renderComponent(node, isRoot, context) {
-  const { write, next, userContext } = context
-
-  // check cache hit
-  const Ctor = node.componentOptions.Ctor
-  const getKey = Ctor.options.serverCacheKey
-  const name = Ctor.options.name
-  const cache = context.cache
-  const registerComponent = registerComponentForCache(Ctor.options, write)
-
-  if (isDef(getKey) && isDef(cache) && isDef(name)) {
-    const rawKey = getKey(node.componentOptions.propsData)
-    if (rawKey === false) {
-      renderComponentInner(node, isRoot, context)
-      return
-    }
-    const key = name + '::' + rawKey
-    const { has, get } = context
-    if (isDef(has)) {
-      has(key, hit => {
-        if (hit === true && isDef(get)) {
-          get(key, res => {
-            if (isDef(registerComponent)) {
-              registerComponent(userContext)
-            }
-            res.components.forEach(register => register(userContext))
-            write(res.html, next)
-          })
-        } else {
-          renderComponentWithCache(node, isRoot, key, context)
-        }
-      })
-    } else if (isDef(get)) {
-      get(key, res => {
-        if (isDef(res)) {
-          if (isDef(registerComponent)) {
-            registerComponent(userContext)
-          }
-          res.components.forEach(register => register(userContext))
-          write(res.html, next)
-        } else {
-          renderComponentWithCache(node, isRoot, key, context)
-        }
-      })
-    }
-  } else {
-    if (isDef(getKey) && isUndef(cache)) {
-      warnOnce(
-        `[vue-server-renderer] Component ${
-          Ctor.options.name || '(anonymous)'
-        } implemented serverCacheKey, ` +
-          'but no cache was provided to the renderer.'
-      )
-    }
-    if (isDef(getKey) && isUndef(name)) {
-      warnOnce(
-        `[vue-server-renderer] Components that implement "serverCacheKey" ` +
-          `must also define a unique "name" option.`
-      )
-    }
-    renderComponentInner(node, isRoot, context)
-  }
-}
-
-function renderComponentWithCache(node, isRoot, key, context) {
-  const write = context.write
-  write.caching = true
-  const buffer = write.cacheBuffer
-  const bufferIndex = buffer.push('') - 1
-  const componentBuffer = write.componentBuffer
-  componentBuffer.push(new Set())
-  context.renderStates.push({
-    type: 'ComponentWithCache',
-    key,
-    buffer,
-    bufferIndex,
-    componentBuffer
-  })
-  renderComponentInner(node, isRoot, context)
-}
-
-function renderComponentInner(node, isRoot, context) {
-  const prevActive = context.activeInstance
-  // expose userContext on vnode
-  node.ssrContext = context.userContext
-  const child = (context.activeInstance = createComponentInstanceForVnode(
-    node,
-    context.activeInstance
-  ))
-  normalizeRender(child)
-
-  const resolve = () => {
-    const childNode = child._render()
-    childNode.parent = node
-    context.renderStates.push({
-      type: 'Component',
-      prevActive
-    })
-    if (isDef(node.data) && isDef(node.data.directives)) {
-      childNode.data = childNode.data || {}
-      childNode.data.directives = node.data.directives
-      childNode.isComponentRootElement = true
-    }
-    renderNode(childNode, isRoot, context)
-  }
-
-  const reject = context.done
-
-  waitForServerPrefetch(child, resolve, reject)
-}
-
-function renderAsyncComponent(node, isRoot, context) {
-  const factory = node.asyncFactory
-
-  const resolve = comp => {
-    if (comp.__esModule && comp.default) {
-      comp = comp.default
-    }
-    const { data, children, tag } = node.asyncMeta
-    const nodeContext = node.asyncMeta.context
-    const resolvedNode: any = createComponent(
-      comp,
-      data,
-      nodeContext,
-      children,
-      tag
-    )
-    if (resolvedNode) {
-      if (resolvedNode.componentOptions) {
-        // normal component
-        renderComponent(resolvedNode, isRoot, context)
-      } else if (!Array.isArray(resolvedNode)) {
-        // single return node from functional component
-        renderNode(resolvedNode, isRoot, context)
-      } else {
-        // multiple return nodes from functional component
-        context.renderStates.push({
-          type: 'Fragment',
-          children: resolvedNode,
-          rendered: 0,
-          total: resolvedNode.length
-        })
-        context.next()
-      }
-    } else {
-      // invalid component, but this does not throw on the client
-      // so render empty comment node
-      context.write(`<!---->`, context.next)
-    }
-  }
-
-  if (factory.resolved) {
-    resolve(factory.resolved)
-    return
-  }
-
-  const reject = context.done
-  let res
-  try {
-    res = factory(resolve, reject)
-  } catch (e: any) {
-    reject(e)
-  }
-  if (res) {
-    if (typeof res.then === 'function') {
-      res.then(resolve, reject).catch(reject)
-    } else {
-      // new syntax in 2.3
-      const comp = res.component
-      if (comp && typeof comp.then === 'function') {
-        comp.then(resolve, reject).catch(reject)
-      }
-    }
-  }
-}
-
-function renderStringNode(el, context) {
-  const { write, next } = context
-  if (isUndef(el.children) || el.children.length === 0) {
-    write(el.open + (el.close || ''), next)
-  } else {
-    const children: Array<VNode> = el.children
-    context.renderStates.push({
-      type: 'Element',
-      children,
-      rendered: 0,
-      total: children.length,
-      endTag: el.close
-    })
-    write(el.open, next)
-  }
-}
-
-function renderElement(el, isRoot, context) {
-  const { write, next } = context
-
-  if (isTrue(isRoot)) {
-    if (!el.data) el.data = {}
-    if (!el.data.attrs) el.data.attrs = {}
-    el.data.attrs[SSR_ATTR] = 'true'
-  }
-
-  if (el.fnOptions) {
-    registerComponentForCache(el.fnOptions, write)
-  }
-
-  const startTag = renderStartingTag(el, context)
-  const endTag = `</${el.tag}>`
-  if (context.isUnaryTag(el.tag)) {
-    write(startTag, next)
-  } else if (isUndef(el.children) || el.children.length === 0) {
-    write(startTag + endTag, next)
-  } else {
-    const children: Array<VNode> = el.children
-    context.renderStates.push({
-      type: 'Element',
-      children,
-      rendered: 0,
-      total: children.length,
-      endTag
-    })
-    write(startTag, next)
-  }
-}
-
-function hasAncestorData(node: VNode) {
-  const parentNode = node.parent
-  return (
-    isDef(parentNode) && (isDef(parentNode.data) || hasAncestorData(parentNode))
-  )
-}
-
-function getVShowDirectiveInfo(node: VNode): VNodeDirective | undefined {
-  let dir: VNodeDirective
-  let tmp
-
-  while (isDef(node)) {
-    if (node.data && node.data.directives) {
-      tmp = node.data.directives.find(dir => dir.name === 'show')
-      if (tmp) {
-        dir = tmp
-      }
-    }
-    node = node.parent!
-  }
-  //@ts-expect-error
-  return dir
-}
-
-function renderStartingTag(node: VNode, context) {
-  let markup = `<${node.tag}`
-  const { directives, modules } = context
-
-  // construct synthetic data for module processing
-  // because modules like style also produce code by parent VNode data
-  if (isUndef(node.data) && hasAncestorData(node)) {
-    node.data = {}
-  }
-  if (isDef(node.data)) {
-    // check directives
-    const dirs = node.data.directives
-    if (dirs) {
-      for (let i = 0; i < dirs.length; i++) {
-        const name = dirs[i].name
-        if (name !== 'show') {
-          const dirRenderer = resolveAsset(context, 'directives', name)
-          if (dirRenderer) {
-            // directives mutate the node's data
-            // which then gets rendered by modules
-            dirRenderer(
-              node.isComponentRootElement ? node.parent : node,
-              dirs[i]
-            )
-          }
-        }
-      }
-    }
-
-    // v-show directive needs to be merged from parent to child
-    const vshowDirectiveInfo = getVShowDirectiveInfo(node)
-    if (vshowDirectiveInfo) {
-      directives.show(node, vshowDirectiveInfo)
-    }
-
-    // apply other modules
-    for (let i = 0; i < modules.length; i++) {
-      const res = modules[i](node)
-      if (res) {
-        markup += res
-      }
-    }
-  }
-  // attach scoped CSS ID
-  let scopeId
-  const activeInstance = context.activeInstance
-  if (
-    isDef(activeInstance) &&
-    activeInstance !== node.context &&
-    isDef((scopeId = activeInstance.$options._scopeId))
-  ) {
-    markup += ` ${scopeId as any}`
-  }
-  if (isDef(node.fnScopeId)) {
-    markup += ` ${node.fnScopeId}`
-  } else {
-    while (isDef(node)) {
-      //@ts-expect-error
-      if (isDef((scopeId = node.context.$options._scopeId))) {
-        markup += ` ${scopeId}`
-      }
-      node = node.parent!
-    }
-  }
-  return markup + '>'
-}
-
-export function createRenderFunction(
-  modules: Array<(node: VNode) => string | null>,
-  directives: Object,
-  isUnaryTag: Function,
-  cache: any
-) {
-  return function render(
-    component: Component,
-    write: (text: string, next: Function) => void,
-    userContext: Record<string, any> | null,
-    done: Function
-  ) {
-    warned = Object.create(null)
-    const context = new RenderContext({
-      activeInstance: component,
-      userContext,
-      write,
-      done,
-      renderNode,
-      isUnaryTag,
-      modules,
-      directives,
-      cache
-    })
-    installSSRHelpers(component)
-    normalizeRender(component)
-
-    const resolve = () => {
-      renderNode(component._render(), true, context)
-    }
-    waitForServerPrefetch(component, resolve, done)
-  }
-}
diff --git a/packages/server-renderer/src/template-renderer/create-async-file-mapper.ts b/packages/server-renderer/src/template-renderer/create-async-file-mapper.ts
deleted file mode 100644
index e09fc3a47ff..00000000000
--- a/packages/server-renderer/src/template-renderer/create-async-file-mapper.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Creates a mapper that maps components used during a server-side render
- * to async chunk files in the client-side build, so that we can inline them
- * directly in the rendered HTML to avoid waterfall requests.
- */
-
-import type { ClientManifest } from './index'
-
-export type AsyncFileMapper = (files: Array<string>) => Array<string>
-
-export function createMapper(clientManifest: ClientManifest): AsyncFileMapper {
-  const map = createMap(clientManifest)
-  // map server-side moduleIds to client-side files
-  return function mapper(moduleIds: Array<string>): Array<string> {
-    const res = new Set<string>()
-    for (let i = 0; i < moduleIds.length; i++) {
-      const mapped = map.get(moduleIds[i])
-      if (mapped) {
-        for (let j = 0; j < mapped.length; j++) {
-          res.add(mapped[j])
-        }
-      }
-    }
-    return Array.from(res)
-  }
-}
-
-function createMap(clientManifest) {
-  const map = new Map()
-  Object.keys(clientManifest.modules).forEach(id => {
-    map.set(id, mapIdToFile(id, clientManifest))
-  })
-  return map
-}
-
-function mapIdToFile(id, clientManifest) {
-  const files: string[] = []
-  const fileIndices = clientManifest.modules[id]
-  if (fileIndices) {
-    fileIndices.forEach(index => {
-      const file = clientManifest.all[index]
-      // only include async files or non-js, non-css assets
-      if (
-        file &&
-        (clientManifest.async.indexOf(file) > -1 ||
-          !/\.(js|css)($|\?)/.test(file))
-      ) {
-        files.push(file)
-      }
-    })
-  }
-  return files
-}
diff --git a/packages/server-renderer/src/template-renderer/index.ts b/packages/server-renderer/src/template-renderer/index.ts
deleted file mode 100644
index 124ece4cf51..00000000000
--- a/packages/server-renderer/src/template-renderer/index.ts
+++ /dev/null
@@ -1,306 +0,0 @@
-const path = require('path')
-const serialize = require('serialize-javascript')
-
-import { isJS, isCSS } from '../util'
-import TemplateStream from './template-stream'
-import { parseTemplate } from './parse-template'
-import { createMapper } from './create-async-file-mapper'
-import type { ParsedTemplate } from './parse-template'
-import type { AsyncFileMapper } from './create-async-file-mapper'
-
-type TemplateRendererOptions = {
-  template?:
-    | string
-    | ((content: string, context: any) => string | Promise<string>)
-  inject?: boolean
-  clientManifest?: ClientManifest
-  shouldPreload?: (file: string, type: string) => boolean
-  shouldPrefetch?: (file: string, type: string) => boolean
-  serializer?: Function
-}
-
-export type ClientManifest = {
-  publicPath: string
-  all: Array<string>
-  initial: Array<string>
-  async: Array<string>
-  modules: {
-    [id: string]: Array<number>
-  }
-  hasNoCssVersion?: {
-    [file: string]: boolean
-  }
-}
-
-type Resource = {
-  file: string
-  extension: string
-  fileWithoutQuery: string
-  asType: string
-}
-
-export default class TemplateRenderer {
-  options: TemplateRendererOptions
-  inject: boolean
-  parsedTemplate: ParsedTemplate | Function | null
-  //@ts-expect-error
-  publicPath: string
-  //@ts-expect-error
-  clientManifest: ClientManifest
-  //@ts-expect-error
-  preloadFiles: Array<Resource>
-  //@ts-expect-error
-  prefetchFiles: Array<Resource>
-  //@ts-expect-error
-  mapFiles: AsyncFileMapper
-  serialize: Function
-
-  constructor(options: TemplateRendererOptions) {
-    this.options = options
-    this.inject = options.inject !== false
-    // if no template option is provided, the renderer is created
-    // as a utility object for rendering assets like preload links and scripts.
-
-    const { template } = options
-    this.parsedTemplate = template
-      ? typeof template === 'string'
-        ? parseTemplate(template)
-        : template
-      : null
-
-    // function used to serialize initial state JSON
-    this.serialize =
-      options.serializer ||
-      (state => {
-        return serialize(state, { isJSON: true })
-      })
-
-    // extra functionality with client manifest
-    if (options.clientManifest) {
-      const clientManifest = (this.clientManifest = options.clientManifest)
-      // ensure publicPath ends with /
-      this.publicPath =
-        clientManifest.publicPath === ''
-          ? ''
-          : clientManifest.publicPath.replace(/([^\/])$/, '$1/')
-      // preload/prefetch directives
-      this.preloadFiles = (clientManifest.initial || []).map(normalizeFile)
-      this.prefetchFiles = (clientManifest.async || []).map(normalizeFile)
-      // initial async chunk mapping
-      this.mapFiles = createMapper(clientManifest)
-    }
-  }
-
-  bindRenderFns(context: Record<string, any>) {
-    const renderer: any = this
-    ;['ResourceHints', 'State', 'Scripts', 'Styles'].forEach(type => {
-      context[`render${type}`] = renderer[`render${type}`].bind(
-        renderer,
-        context
-      )
-    })
-    // also expose getPreloadFiles, useful for HTTP/2 push
-    context.getPreloadFiles = renderer.getPreloadFiles.bind(renderer, context)
-  }
-
-  // render synchronously given rendered app content and render context
-  render(
-    content: string,
-    context: Record<string, any> | null
-  ): string | Promise<string> {
-    const template = this.parsedTemplate
-    if (!template) {
-      throw new Error('render cannot be called without a template.')
-    }
-    context = context || {}
-
-    if (typeof template === 'function') {
-      return template(content, context)
-    }
-
-    if (this.inject) {
-      return (
-        template.head(context) +
-        (context.head || '') +
-        this.renderResourceHints(context) +
-        this.renderStyles(context) +
-        template.neck(context) +
-        content +
-        this.renderState(context) +
-        this.renderScripts(context) +
-        template.tail(context)
-      )
-    } else {
-      return (
-        template.head(context) +
-        template.neck(context) +
-        content +
-        template.tail(context)
-      )
-    }
-  }
-
-  renderStyles(context: Record<string, any>): string {
-    const initial = this.preloadFiles || []
-    const async = this.getUsedAsyncFiles(context) || []
-    const cssFiles = initial.concat(async).filter(({ file }) => isCSS(file))
-    return (
-      // render links for css files
-      (cssFiles.length
-        ? cssFiles
-            .map(
-              ({ file }) =>
-                `<link rel="stylesheet" href="${this.publicPath}${file}">`
-            )
-            .join('')
-        : '') +
-      // context.styles is a getter exposed by vue-style-loader which contains
-      // the inline component styles collected during SSR
-      (context.styles || '')
-    )
-  }
-
-  renderResourceHints(context: Object): string {
-    return this.renderPreloadLinks(context) + this.renderPrefetchLinks(context)
-  }
-
-  getPreloadFiles(context: Object): Array<Resource> {
-    const usedAsyncFiles = this.getUsedAsyncFiles(context)
-    if (this.preloadFiles || usedAsyncFiles) {
-      return (this.preloadFiles || []).concat(usedAsyncFiles || [])
-    } else {
-      return []
-    }
-  }
-
-  renderPreloadLinks(context: Object): string {
-    const files = this.getPreloadFiles(context)
-    const shouldPreload = this.options.shouldPreload
-    if (files.length) {
-      return files
-        .map(({ file, extension, fileWithoutQuery, asType }) => {
-          let extra = ''
-          // by default, we only preload scripts or css
-          if (!shouldPreload && asType !== 'script' && asType !== 'style') {
-            return ''
-          }
-          // user wants to explicitly control what to preload
-          if (shouldPreload && !shouldPreload(fileWithoutQuery, asType)) {
-            return ''
-          }
-          if (asType === 'font') {
-            extra = ` type="font/${extension}" crossorigin`
-          }
-          return `<link rel="preload" href="${this.publicPath}${file}"${
-            asType !== '' ? ` as="${asType}"` : ''
-          }${extra}>`
-        })
-        .join('')
-    } else {
-      return ''
-    }
-  }
-
-  renderPrefetchLinks(context: Object): string {
-    const shouldPrefetch = this.options.shouldPrefetch
-    if (this.prefetchFiles) {
-      const usedAsyncFiles = this.getUsedAsyncFiles(context)
-      const alreadyRendered = file => {
-        return usedAsyncFiles && usedAsyncFiles.some(f => f.file === file)
-      }
-      return this.prefetchFiles
-        .map(({ file, fileWithoutQuery, asType }) => {
-          if (shouldPrefetch && !shouldPrefetch(fileWithoutQuery, asType)) {
-            return ''
-          }
-          if (alreadyRendered(file)) {
-            return ''
-          }
-          return `<link rel="prefetch" href="${this.publicPath}${file}">`
-        })
-        .join('')
-    } else {
-      return ''
-    }
-  }
-
-  renderState(
-    context: Record<string, any>,
-    options?: Record<string, any>
-  ): string {
-    const { contextKey = 'state', windowKey = '__INITIAL_STATE__' } =
-      options || {}
-    const state = this.serialize(context[contextKey])
-    const autoRemove = __DEV__
-      ? ''
-      : ';(function(){var s;(s=document.currentScript||document.scripts[document.scripts.length-1]).parentNode.removeChild(s);}());'
-    const nonceAttr = context.nonce ? ` nonce="${context.nonce}"` : ''
-    return context[contextKey]
-      ? `<script${nonceAttr}>window.${windowKey}=${state}${autoRemove}</script>`
-      : ''
-  }
-
-  renderScripts(context: Object): string {
-    if (this.clientManifest) {
-      const initial = this.preloadFiles.filter(({ file }) => isJS(file))
-      const async = (this.getUsedAsyncFiles(context) || []).filter(({ file }) =>
-        isJS(file)
-      )
-      const needed = [initial[0]].concat(async, initial.slice(1))
-      return needed
-        .map(({ file }) => {
-          return `<script src="${this.publicPath}${file}" defer></script>`
-        })
-        .join('')
-    } else {
-      return ''
-    }
-  }
-
-  getUsedAsyncFiles(context: Record<string, any>): Array<Resource> | undefined {
-    if (
-      !context._mappedFiles &&
-      context._registeredComponents &&
-      this.mapFiles
-    ) {
-      const registered: any[] = Array.from(context._registeredComponents)
-      context._mappedFiles = this.mapFiles(registered).map(normalizeFile)
-    }
-    return context._mappedFiles
-  }
-
-  // create a transform stream
-  createStream(context: Record<string, any> | undefined): TemplateStream {
-    if (!this.parsedTemplate) {
-      throw new Error('createStream cannot be called without a template.')
-    }
-    //@ts-expect-error
-    return new TemplateStream(this, this.parsedTemplate, context || {})
-  }
-}
-
-function normalizeFile(file: string): Resource {
-  const withoutQuery = file.replace(/\?.*/, '')
-  const extension = path.extname(withoutQuery).slice(1)
-  return {
-    file,
-    extension,
-    fileWithoutQuery: withoutQuery,
-    asType: getPreloadType(extension)
-  }
-}
-
-function getPreloadType(ext: string): string {
-  if (ext === 'js') {
-    return 'script'
-  } else if (ext === 'css') {
-    return 'style'
-  } else if (/jpe?g|png|svg|gif|webp|ico/.test(ext)) {
-    return 'image'
-  } else if (/woff2?|ttf|otf|eot/.test(ext)) {
-    return 'font'
-  } else {
-    // not exhausting all possibilities here, but above covers common cases
-    return ''
-  }
-}
diff --git a/packages/server-renderer/src/template-renderer/parse-template.ts b/packages/server-renderer/src/template-renderer/parse-template.ts
deleted file mode 100644
index 29baf23b546..00000000000
--- a/packages/server-renderer/src/template-renderer/parse-template.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-const compile = require('lodash.template')
-const compileOptions = {
-  escape: /{{([^{][\s\S]+?[^}])}}/g,
-  interpolate: /{{{([\s\S]+?)}}}/g
-}
-
-export type ParsedTemplate = {
-  head: (data: any) => string
-  neck: (data: any) => string
-  tail: (data: any) => string
-}
-
-export function parseTemplate(
-  template: string,
-  contentPlaceholder: string = '<!--vue-ssr-outlet-->'
-): ParsedTemplate {
-  if (typeof template === 'object') {
-    return template
-  }
-
-  let i = template.indexOf('</head>')
-  const j = template.indexOf(contentPlaceholder)
-
-  if (j < 0) {
-    throw new Error(`Content placeholder not found in template.`)
-  }
-
-  if (i < 0) {
-    i = template.indexOf('<body>')
-    if (i < 0) {
-      i = j
-    }
-  }
-
-  return {
-    head: compile(template.slice(0, i), compileOptions),
-    neck: compile(template.slice(i, j), compileOptions),
-    tail: compile(template.slice(j + contentPlaceholder.length), compileOptions)
-  }
-}
diff --git a/packages/server-renderer/src/template-renderer/template-stream.ts b/packages/server-renderer/src/template-renderer/template-stream.ts
deleted file mode 100644
index 38fe7ba37f4..00000000000
--- a/packages/server-renderer/src/template-renderer/template-stream.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-// const Transform = require("stream").Transform;
-import type TemplateRenderer from './index'
-import type { ParsedTemplate } from './parse-template'
-import { Transform } from 'stream'
-
-export default class TemplateStream extends Transform {
-  started: boolean
-  renderer: TemplateRenderer
-  template: ParsedTemplate
-  context: Record<string, any>
-  inject: boolean
-
-  constructor(
-    renderer: TemplateRenderer,
-    template: ParsedTemplate,
-    context: Record<string, any>
-  ) {
-    super()
-    this.started = false
-    this.renderer = renderer
-    this.template = template
-    this.context = context || {}
-    this.inject = renderer.inject
-  }
-
-  _transform(data: Buffer | string, encoding: string, done: Function) {
-    if (!this.started) {
-      this.emit('beforeStart')
-      this.start()
-    }
-    this.push(data)
-    done()
-  }
-
-  start() {
-    this.started = true
-    this.push(this.template.head(this.context))
-
-    if (this.inject) {
-      // inline server-rendered head meta information
-      if (this.context.head) {
-        this.push(this.context.head)
-      }
-
-      // inline preload/prefetch directives for initial/async chunks
-      const links = this.renderer.renderResourceHints(this.context)
-      if (links) {
-        this.push(links)
-      }
-
-      // CSS files and inline server-rendered CSS collected by vue-style-loader
-      const styles = this.renderer.renderStyles(this.context)
-      if (styles) {
-        this.push(styles)
-      }
-    }
-
-    this.push(this.template.neck(this.context))
-  }
-
-  _flush(done: Function) {
-    this.emit('beforeEnd')
-
-    if (this.inject) {
-      // inline initial store state
-      const state = this.renderer.renderState(this.context)
-      if (state) {
-        this.push(state)
-      }
-
-      // embed scripts needed
-      const scripts = this.renderer.renderScripts(this.context)
-      if (scripts) {
-        this.push(scripts)
-      }
-    }
-
-    this.push(this.template.tail(this.context))
-    done()
-  }
-}
diff --git a/packages/server-renderer/src/util.ts b/packages/server-renderer/src/util.ts
deleted file mode 100644
index 777abb659f6..00000000000
--- a/packages/server-renderer/src/util.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import { makeMap } from 'shared/util'
-
-const isAttr = makeMap(
-  'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' +
-    'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,' +
-    'checked,cite,class,code,codebase,color,cols,colspan,content,' +
-    'contenteditable,contextmenu,controls,coords,data,datetime,default,' +
-    'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,for,' +
-    'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,' +
-    'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,' +
-    'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,' +
-    'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,' +
-    'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' +
-    'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' +
-    'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' +
-    'target,title,usemap,value,width,wrap'
-)
-
-const unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/ // eslint-disable-line no-control-regex
-export const isSSRUnsafeAttr = (name: string): boolean => {
-  return unsafeAttrCharRE.test(name)
-}
-
-/* istanbul ignore next */
-const isRenderableAttr = (name: string): boolean => {
-  return (
-    isAttr(name) || name.indexOf('data-') === 0 || name.indexOf('aria-') === 0
-  )
-}
-export { isRenderableAttr }
-
-export const propsToAttrMap = {
-  acceptCharset: 'accept-charset',
-  className: 'class',
-  htmlFor: 'for',
-  httpEquiv: 'http-equiv'
-}
-
-const ESC = {
-  '<': '&lt;',
-  '>': '&gt;',
-  '"': '&quot;',
-  '&': '&amp;'
-}
-
-export function escape(s: string) {
-  return s.replace(/[<>"&]/g, escapeChar)
-}
-
-function escapeChar(a) {
-  return ESC[a] || a
-}
-
-export const noUnitNumericStyleProps = {
-  'animation-iteration-count': true,
-  'border-image-outset': true,
-  'border-image-slice': true,
-  'border-image-width': true,
-  'box-flex': true,
-  'box-flex-group': true,
-  'box-ordinal-group': true,
-  'column-count': true,
-  columns: true,
-  flex: true,
-  'flex-grow': true,
-  'flex-positive': true,
-  'flex-shrink': true,
-  'flex-negative': true,
-  'flex-order': true,
-  'grid-row': true,
-  'grid-row-end': true,
-  'grid-row-span': true,
-  'grid-row-start': true,
-  'grid-column': true,
-  'grid-column-end': true,
-  'grid-column-span': true,
-  'grid-column-start': true,
-  'font-weight': true,
-  'line-clamp': true,
-  'line-height': true,
-  opacity: true,
-  order: true,
-  orphans: true,
-  'tab-size': true,
-  widows: true,
-  'z-index': true,
-  zoom: true,
-  // SVG
-  'fill-opacity': true,
-  'flood-opacity': true,
-  'stop-opacity': true,
-  'stroke-dasharray': true,
-  'stroke-dashoffset': true,
-  'stroke-miterlimit': true,
-  'stroke-opacity': true,
-  'stroke-width': true
-}
-
-export const isJS = (file: string): boolean => /\.js(\?[^.]+)?$/.test(file)
-
-export const isCSS = (file: string): boolean => /\.css(\?[^.]+)?$/.test(file)
-
-export function createPromiseCallback() {
-  let resolve, reject
-  const promise: Promise<string> = new Promise((_resolve, _reject) => {
-    resolve = _resolve
-    reject = _reject
-  })
-  const cb = (err: Error, res?: string) => {
-    if (err) return reject(err)
-    resolve(res || '')
-  }
-  return { promise, cb }
-}
diff --git a/packages/server-renderer/src/webpack-plugin/client.ts b/packages/server-renderer/src/webpack-plugin/client.ts
deleted file mode 100644
index d95061e0279..00000000000
--- a/packages/server-renderer/src/webpack-plugin/client.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-const hash = require('hash-sum')
-const uniq = require('lodash.uniq')
-import { isJS, isCSS, getAssetName, onEmit, stripModuleIdHash } from './util'
-
-export default class VueSSRClientPlugin {
-  constructor(options = {}) {
-    //@ts-expect-error no type on options
-    this.options = Object.assign(
-      {
-        filename: 'vue-ssr-client-manifest.json'
-      },
-      options
-    )
-  }
-
-  apply(compiler) {
-    const stage = 'PROCESS_ASSETS_STAGE_ADDITIONAL'
-    onEmit(compiler, 'vue-client-plugin', stage, (compilation, cb) => {
-      const stats = compilation.getStats().toJson()
-
-      const allFiles = uniq(stats.assets.map(a => a.name))
-
-      const initialFiles = uniq(
-        Object.keys(stats.entrypoints)
-          .map(name => stats.entrypoints[name].assets)
-          .reduce((assets, all) => all.concat(assets), [])
-          .map(getAssetName)
-          .filter(file => isJS(file) || isCSS(file))
-      )
-
-      const asyncFiles = allFiles
-        .filter(file => isJS(file) || isCSS(file))
-        .filter(file => initialFiles.indexOf(file) < 0)
-
-      const manifest = {
-        publicPath: stats.publicPath,
-        all: allFiles,
-        initial: initialFiles,
-        async: asyncFiles,
-        modules: {
-          /* [identifier: string]: Array<index: number> */
-        }
-      }
-
-      const assetModules = stats.modules.filter(m => m.assets.length)
-      const fileToIndex = asset => manifest.all.indexOf(getAssetName(asset))
-      stats.modules.forEach(m => {
-        // ignore modules duplicated in multiple chunks
-        if (m.chunks.length === 1) {
-          const cid = m.chunks[0]
-          const chunk = stats.chunks.find(c => c.id === cid)
-          if (!chunk || !chunk.files) {
-            return
-          }
-          const id = stripModuleIdHash(m.identifier)
-          const files = (manifest.modules[hash(id)] =
-            chunk.files.map(fileToIndex))
-          // find all asset modules associated with the same chunk
-          assetModules.forEach(m => {
-            if (m.chunks.some(id => id === cid)) {
-              files.push.apply(files, m.assets.map(fileToIndex))
-            }
-          })
-        }
-      })
-
-      const json = JSON.stringify(manifest, null, 2)
-      //@ts-expect-error no type on options
-      compilation.assets[this.options.filename] = {
-        source: () => json,
-        size: () => json.length
-      }
-      cb()
-    })
-  }
-}
diff --git a/packages/server-renderer/src/webpack-plugin/server.ts b/packages/server-renderer/src/webpack-plugin/server.ts
deleted file mode 100644
index 439335ef9cd..00000000000
--- a/packages/server-renderer/src/webpack-plugin/server.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { validate, isJS, getAssetName, onEmit } from './util'
-
-export default class VueSSRServerPlugin {
-  constructor(options = {}) {
-    //@ts-expect-error
-    this.options = Object.assign(
-      {
-        filename: 'vue-ssr-server-bundle.json'
-      },
-      options
-    )
-  }
-
-  apply(compiler) {
-    validate(compiler)
-
-    const stage = 'PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER'
-    onEmit(compiler, 'vue-server-plugin', stage, (compilation, cb) => {
-      const stats = compilation.getStats().toJson()
-      const entryName = Object.keys(stats.entrypoints)[0]
-      const entryInfo = stats.entrypoints[entryName]
-
-      if (!entryInfo) {
-        // #5553
-        return cb()
-      }
-
-      const entryAssets = entryInfo.assets.map(getAssetName).filter(isJS)
-
-      if (entryAssets.length > 1) {
-        throw new Error(
-          `Server-side bundle should have one single entry file. ` +
-            `Avoid using CommonsChunkPlugin in the server config.`
-        )
-      }
-
-      const entry = entryAssets[0]
-      if (!entry || typeof entry !== 'string') {
-        throw new Error(
-          `Entry "${entryName}" not found. Did you specify the correct entry option?`
-        )
-      }
-
-      const bundle = {
-        entry,
-        files: {},
-        maps: {}
-      }
-
-      Object.keys(compilation.assets).forEach(name => {
-        if (isJS(name)) {
-          bundle.files[name] = compilation.assets[name].source()
-        } else if (name.match(/\.js\.map$/)) {
-          bundle.maps[name.replace(/\.map$/, '')] = JSON.parse(
-            compilation.assets[name].source()
-          )
-        }
-        // do not emit anything else for server
-        delete compilation.assets[name]
-      })
-
-      const json = JSON.stringify(bundle, null, 2)
-      //@ts-expect-error
-      const filename = this.options.filename
-
-      compilation.assets[filename] = {
-        source: () => json,
-        size: () => json.length
-      }
-
-      cb()
-    })
-  }
-}
diff --git a/packages/server-renderer/src/webpack-plugin/util.ts b/packages/server-renderer/src/webpack-plugin/util.ts
deleted file mode 100644
index 1af5a7f28e5..00000000000
--- a/packages/server-renderer/src/webpack-plugin/util.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-const { red, yellow } = require('chalk')
-const webpack = require('webpack')
-
-const prefix = `[vue-server-renderer-webpack-plugin]`
-const warn = (exports.warn = msg => console.error(red(`${prefix} ${msg}\n`)))
-const tip = (exports.tip = msg => console.log(yellow(`${prefix} ${msg}\n`)))
-
-const isWebpack5 = !!(webpack.version && webpack.version[0] > 4)
-
-export const validate = compiler => {
-  if (compiler.options.target !== 'node') {
-    warn('webpack config `target` should be "node".')
-  }
-
-  if (compiler.options.output) {
-    if (compiler.options.output.library) {
-      // Webpack >= 5.0.0
-      if (compiler.options.output.library.type !== 'commonjs2') {
-        warn('webpack config `output.library.type` should be "commonjs2".')
-      }
-    } else if (compiler.options.output.libraryTarget !== 'commonjs2') {
-      // Webpack < 5.0.0
-      warn('webpack config `output.libraryTarget` should be "commonjs2".')
-    }
-  }
-
-  if (!compiler.options.externals) {
-    tip(
-      'It is recommended to externalize dependencies in the server build for ' +
-        'better build performance.'
-    )
-  }
-}
-
-export const onEmit = (compiler, name, stageName, hook) => {
-  if (isWebpack5) {
-    // Webpack >= 5.0.0
-    compiler.hooks.compilation.tap(name, compilation => {
-      if (compilation.compiler !== compiler) {
-        // Ignore child compilers
-        return
-      }
-      const stage = webpack.Compilation[stageName]
-      compilation.hooks.processAssets.tapAsync(
-        { name, stage },
-        (assets, cb) => {
-          hook(compilation, cb)
-        }
-      )
-    })
-  } else if (compiler.hooks) {
-    // Webpack >= 4.0.0
-    compiler.hooks.emit.tapAsync(name, hook)
-  } else {
-    // Webpack < 4.0.0
-    compiler.plugin('emit', hook)
-  }
-}
-
-export const stripModuleIdHash = id => {
-  if (isWebpack5) {
-    // Webpack >= 5.0.0
-    return id.replace(/\|\w+$/, '')
-  }
-  // Webpack < 5.0.0
-  return id.replace(/\s\w+$/, '')
-}
-
-export const getAssetName = asset => {
-  if (typeof asset === 'string') {
-    return asset
-  }
-  return asset.name
-}
-
-export { isJS, isCSS } from '../util'
diff --git a/packages/server-renderer/src/write.ts b/packages/server-renderer/src/write.ts
deleted file mode 100644
index 925c0f96588..00000000000
--- a/packages/server-renderer/src/write.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-const MAX_STACK_DEPTH = 800
-const noop = _ => _
-
-const defer =
-  typeof process !== 'undefined' && process.nextTick
-    ? process.nextTick
-    : typeof Promise !== 'undefined'
-    ? fn => Promise.resolve().then(fn)
-    : typeof setTimeout !== 'undefined'
-    ? setTimeout
-    : noop
-
-if (defer === noop) {
-  throw new Error(
-    'Your JavaScript runtime does not support any asynchronous primitives ' +
-      'that are required by vue-server-renderer. Please use a polyfill for ' +
-      'either Promise or setTimeout.'
-  )
-}
-
-export function createWriteFunction(
-  write: (text: string, next: Function) => boolean,
-  onError: Function
-): Function {
-  let stackDepth = 0
-  const cachedWrite = (text, next) => {
-    if (text && cachedWrite.caching) {
-      cachedWrite.cacheBuffer[cachedWrite.cacheBuffer.length - 1] += text
-    }
-    const waitForNext = write(text, next)
-    if (waitForNext !== true) {
-      if (stackDepth >= MAX_STACK_DEPTH) {
-        defer(() => {
-          try {
-            next()
-          } catch (e: any) {
-            onError(e)
-          }
-        })
-      } else {
-        stackDepth++
-        next()
-        stackDepth--
-      }
-    }
-  }
-  cachedWrite.caching = false
-  cachedWrite.cacheBuffer = []
-  cachedWrite.componentBuffer = []
-  return cachedWrite
-}
diff --git a/packages/server-renderer/test/async-loader.js b/packages/server-renderer/test/async-loader.js
deleted file mode 100644
index 41b4a98a89f..00000000000
--- a/packages/server-renderer/test/async-loader.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const hash = require('hash-sum')
-
-module.exports = function (code) {
-  const id = hash(this.request) // simulating vue-loader module id injection
-  return code.replace('__MODULE_ID__', id)
-}
diff --git a/packages/server-renderer/test/compile-with-webpack.ts b/packages/server-renderer/test/compile-with-webpack.ts
deleted file mode 100644
index 37467595619..00000000000
--- a/packages/server-renderer/test/compile-with-webpack.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import path from 'path'
-import webpack from 'webpack'
-import MemoryFS from 'memory-fs'
-import { RenderOptions } from 'server/create-renderer'
-import { createBundleRenderer } from 'server/index'
-import VueSSRServerPlugin from 'server/webpack-plugin/server'
-
-export function compileWithWebpack(
-  file: string,
-  extraConfig?: webpack.Configuration
-) {
-  const config: webpack.Configuration = {
-    mode: 'development',
-    entry: path.resolve(__dirname, 'fixtures', file),
-    module: {
-      rules: [
-        {
-          test: /async-.*\.js$/,
-          loader: require.resolve('./async-loader')
-        },
-        {
-          test: /\.(png|woff2|css)$/,
-          loader: require.resolve('file-loader'),
-          options: {
-            name: '[name].[ext]'
-          }
-        }
-      ]
-    }
-  }
-  if (extraConfig) {
-    Object.assign(config, extraConfig)
-  }
-
-  const compiler = webpack(config)
-  const fs = new MemoryFS()
-  compiler.outputFileSystem = fs
-
-  return new Promise<MemoryFS>((resolve, reject) => {
-    compiler.run(err => {
-      if (err) {
-        reject(err)
-      } else {
-        resolve(fs)
-      }
-    })
-  })
-}
-
-export async function createWebpackBundleRenderer(
-  file: string,
-  options?: RenderOptions & { asBundle?: boolean }
-) {
-  const asBundle = !!(options && options.asBundle)
-  if (options) delete options.asBundle
-
-  const fs = await compileWithWebpack(file, {
-    target: 'node',
-    devtool: asBundle ? 'source-map' : false,
-    output: {
-      path: '/',
-      filename: 'bundle.js',
-      libraryTarget: 'commonjs2'
-    },
-    externals: [require.resolve('../../../dist/vue.runtime.common.js')],
-    plugins: asBundle ? [new VueSSRServerPlugin()] : []
-  })
-
-  const bundle = asBundle
-    ? JSON.parse(fs.readFileSync('/vue-ssr-server-bundle.json', 'utf-8'))
-    : fs.readFileSync('/bundle.js', 'utf-8')
-  return createBundleRenderer(bundle, options)
-}
diff --git a/packages/server-renderer/test/fixtures/app.js b/packages/server-renderer/test/fixtures/app.js
deleted file mode 100644
index 08c758522ef..00000000000
--- a/packages/server-renderer/test/fixtures/app.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import Vue from '../../../../dist/vue.runtime.common.js'
-
-export default context => {
-  return new Promise(resolve => {
-    context.msg = 'hello'
-    resolve(
-      new Vue({
-        render(h) {
-          return h('div', context.url)
-        }
-      })
-    )
-  })
-}
diff --git a/packages/server-renderer/test/fixtures/async-bar.js b/packages/server-renderer/test/fixtures/async-bar.js
deleted file mode 100644
index 23349f8a9ee..00000000000
--- a/packages/server-renderer/test/fixtures/async-bar.js
+++ /dev/null
@@ -1,8 +0,0 @@
-module.exports = {
-  beforeCreate() {
-    this.$vnode.ssrContext._registeredComponents.add('__MODULE_ID__')
-  },
-  render(h) {
-    return h('div', 'async bar')
-  }
-}
diff --git a/packages/server-renderer/test/fixtures/async-foo.js b/packages/server-renderer/test/fixtures/async-foo.js
deleted file mode 100644
index 18e5b235e41..00000000000
--- a/packages/server-renderer/test/fixtures/async-foo.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// import image and font
-import './test.css'
-import font from './test.woff2'
-import image from './test.png'
-
-export default {
-  beforeCreate() {
-    this.$vnode.ssrContext._registeredComponents.add('__MODULE_ID__')
-  },
-  render(h) {
-    return h('div', `async ${font} ${image}`)
-  }
-}
diff --git a/packages/server-renderer/test/fixtures/cache-opt-out.js b/packages/server-renderer/test/fixtures/cache-opt-out.js
deleted file mode 100644
index 7c0d1247280..00000000000
--- a/packages/server-renderer/test/fixtures/cache-opt-out.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import Vue from '../../../../dist/vue.runtime.common.js'
-
-const app = {
-  name: 'app',
-  props: ['id'],
-  serverCacheKey: props => (props.id === 1 ? false : props.id),
-  render(h) {
-    return h('div', '/test')
-  }
-}
-
-export default () => {
-  return Promise.resolve(
-    new Vue({
-      render: h => h(app, { props: { id: 1 } })
-    })
-  )
-}
diff --git a/packages/server-renderer/test/fixtures/cache.js b/packages/server-renderer/test/fixtures/cache.js
deleted file mode 100644
index 48d1410b2b3..00000000000
--- a/packages/server-renderer/test/fixtures/cache.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import Vue from '../../../../dist/vue.runtime.common.js'
-
-const app = {
-  name: 'app',
-  props: ['id'],
-  serverCacheKey: props => props.id,
-  render(h) {
-    return h('div', '/test')
-  }
-}
-
-export default () => {
-  return Promise.resolve(
-    new Vue({
-      render: h => h(app, { props: { id: 1 } })
-    })
-  )
-}
diff --git a/packages/server-renderer/test/fixtures/error.js b/packages/server-renderer/test/fixtures/error.js
deleted file mode 100644
index a4548e3802c..00000000000
--- a/packages/server-renderer/test/fixtures/error.js
+++ /dev/null
@@ -1 +0,0 @@
-throw new Error('foo')
diff --git a/packages/server-renderer/test/fixtures/nested-cache.js b/packages/server-renderer/test/fixtures/nested-cache.js
deleted file mode 100644
index be72490d666..00000000000
--- a/packages/server-renderer/test/fixtures/nested-cache.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import Vue from '../../../../dist/vue.runtime.common.js'
-
-function createRegisterFn(id) {
-  return function (context) {
-    context = context || this.$vnode.ssrContext
-    context.registered.push(id)
-  }
-}
-
-function addHooks(comp) {
-  const hook = createRegisterFn(comp.name)
-  return Object.assign(comp, {
-    _ssrRegister: hook,
-    beforeCreate: hook
-  })
-}
-
-const grandchild = addHooks({
-  name: 'grandchild',
-  props: ['id'],
-  serverCacheKey: props => props.id,
-  render(h) {
-    return h('div', '/test')
-  }
-})
-
-const child = addHooks({
-  name: 'child',
-  props: ['id'],
-  serverCacheKey: props => props.id,
-  render(h) {
-    return h(grandchild, { props: { id: this.id } })
-  }
-})
-
-const app = addHooks({
-  name: 'app',
-  props: ['id'],
-  serverCacheKey: props => props.id,
-  render(h) {
-    return h(child, { props: { id: this.id } })
-  }
-})
-
-export default () => {
-  return Promise.resolve(
-    new Vue({
-      render: h => h(app, { props: { id: 1 } })
-    })
-  )
-}
diff --git a/packages/server-renderer/test/fixtures/promise-rejection.js b/packages/server-renderer/test/fixtures/promise-rejection.js
deleted file mode 100644
index 40dcccf3dd1..00000000000
--- a/packages/server-renderer/test/fixtures/promise-rejection.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default () => {
-  return Promise.reject(new Error('foo'))
-}
diff --git a/packages/server-renderer/test/fixtures/split.js b/packages/server-renderer/test/fixtures/split.js
deleted file mode 100644
index 1d33acc4abe..00000000000
--- a/packages/server-renderer/test/fixtures/split.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import Vue from '../../../../dist/vue.runtime.common.js'
-
-// async component!
-const Foo = () => import('./async-foo')
-const Bar = () => import('./async-bar') // eslint-disable-line
-
-export default context => {
-  return new Promise(resolve => {
-    context.msg = 'hello'
-    const vm = new Vue({
-      render(h) {
-        return h('div', [context.url, h(Foo)])
-      }
-    })
-
-    // simulate router.onReady
-    Foo().then(comp => {
-      // resolve now to make the render sync
-      Foo.resolved = Vue.extend(comp.default)
-      resolve(vm)
-    })
-  })
-}
diff --git a/packages/server-renderer/test/fixtures/test.css b/packages/server-renderer/test/fixtures/test.css
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/packages/server-renderer/test/fixtures/test.png b/packages/server-renderer/test/fixtures/test.png
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/packages/server-renderer/test/fixtures/test.woff2 b/packages/server-renderer/test/fixtures/test.woff2
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/packages/server-renderer/test/ssr-basic-renderer.spec.ts b/packages/server-renderer/test/ssr-basic-renderer.spec.ts
deleted file mode 100644
index a26820af5be..00000000000
--- a/packages/server-renderer/test/ssr-basic-renderer.spec.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-// @vitest-environment node
-
-import Vue from 'vue'
-import renderToString from 'server/index-basic'
-import { _it } from './utils'
-
-describe('SSR: basicRenderer', () => {
-  _it('should work', done => {
-    renderToString(
-      new Vue({
-        template: `
-        <div>
-          <p class="hi">yoyo</p>
-          <div id="ho" :class="{ red: isRed }"></div>
-          <span>{{ test }}</span>
-          <input :value="test">
-          <img :src="imageUrl">
-          <test></test>
-          <test-async></test-async>
-        </div>
-      `,
-        data: {
-          test: 'hi',
-          isRed: true,
-          imageUrl: 'https://vuejs.org/images/logo.png'
-        },
-        components: {
-          test: {
-            render() {
-              return this.$createElement('div', { class: ['a'] }, 'test')
-            }
-          },
-          testAsync(resolve) {
-            resolve({
-              render() {
-                return this.$createElement(
-                  'span',
-                  { class: ['b'] },
-                  'testAsync'
-                )
-              }
-            })
-          }
-        }
-      }),
-      (err, result) => {
-        expect(err).toBeNull()
-        expect(result).toContain(
-          '<div data-server-rendered="true">' +
-            '<p class="hi">yoyo</p> ' +
-            '<div id="ho" class="red"></div> ' +
-            '<span>hi</span> ' +
-            '<input value="hi"> ' +
-            '<img src="https://vuejs.org/images/logo.png"> ' +
-            '<div class="a">test</div> ' +
-            '<span class="b">testAsync</span>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  // #5941
-  _it('should work properly when accessing $ssrContext in root component', done => {
-    let ssrContext
-    renderToString(
-      new Vue({
-        template: `
-        <div></div>
-      `,
-        created() {
-          ssrContext = this.$ssrContext
-        }
-      }),
-      err => {
-        expect(err).toBeNull()
-        expect(ssrContext).toBeUndefined()
-        done()
-      }
-    )
-  })
-})
diff --git a/packages/server-renderer/test/ssr-bundle-render.spec.ts b/packages/server-renderer/test/ssr-bundle-render.spec.ts
deleted file mode 100644
index 608fc675c64..00000000000
--- a/packages/server-renderer/test/ssr-bundle-render.spec.ts
+++ /dev/null
@@ -1,325 +0,0 @@
-// @vitest-environment node
-
-import { createWebpackBundleRenderer } from './compile-with-webpack'
-
-describe('SSR: bundle renderer', () => {
-  createAssertions(true)
-  // createAssertions(false)
-})
-
-function createAssertions(runInNewContext) {
-  it('renderToString', async () => {
-    const renderer = await createWebpackBundleRenderer('app.js', {
-      runInNewContext
-    })
-    const context: any = { url: '/test' }
-    const res = await renderer.renderToString(context)
-    expect(res).toBe('<div data-server-rendered="true">/test</div>')
-    expect(context.msg).toBe('hello')
-  })
-
-  it('renderToStream', async () => {
-    const renderer = await createWebpackBundleRenderer('app.js', {
-      runInNewContext
-    })
-    const context: any = { url: '/test' }
-
-    const res = await new Promise((resolve, reject) => {
-      const stream = renderer.renderToStream(context)
-      let res = ''
-      stream.on('data', chunk => {
-        res += chunk.toString()
-      })
-      stream.on('error', reject)
-      stream.on('end', () => {
-        resolve(res)
-      })
-    })
-
-    expect(res).toBe('<div data-server-rendered="true">/test</div>')
-    expect(context.msg).toBe('hello')
-  })
-
-  it('renderToString catch error', async () => {
-    const renderer = await createWebpackBundleRenderer('error.js', {
-      runInNewContext
-    })
-    try {
-      await renderer.renderToString()
-    } catch (err: any) {
-      expect(err.message).toBe('foo')
-    }
-  })
-
-  it('renderToString catch Promise rejection', async () => {
-    const renderer = await createWebpackBundleRenderer('promise-rejection.js', {
-      runInNewContext
-    })
-    try {
-      await renderer.renderToString()
-    } catch (err: any) {
-      expect(err.message).toBe('foo')
-    }
-  })
-
-  it('renderToStream catch error', async () => {
-    const renderer = await createWebpackBundleRenderer('error.js', {
-      runInNewContext
-    })
-
-    const err = await new Promise<Error>(resolve => {
-      const stream = renderer.renderToStream()
-      stream.on('error', resolve)
-    })
-
-    expect(err.message).toBe('foo')
-  })
-
-  it('renderToStream catch Promise rejection', async () => {
-    const renderer = await createWebpackBundleRenderer('promise-rejection.js', {
-      runInNewContext
-    })
-
-    const err = await new Promise<Error>(resolve => {
-      const stream = renderer.renderToStream()
-      stream.on('error', resolve)
-    })
-
-    expect(err.message).toBe('foo')
-  })
-
-  it('render with cache (get/set)', async () => {
-    const cache = {}
-    const get = vi.fn()
-    const set = vi.fn()
-    const options = {
-      runInNewContext,
-      cache: {
-        // async
-        get: (key, cb) => {
-          setTimeout(() => {
-            get(key)
-            cb(cache[key])
-          }, 0)
-        },
-        set: (key, val) => {
-          set(key, val)
-          cache[key] = val
-        }
-      }
-    }
-    const renderer = await createWebpackBundleRenderer('cache.js', options)
-    const expected = '<div data-server-rendered="true">/test</div>'
-    const key = 'app::1'
-    const res = await renderer.renderToString()
-
-    expect(res).toBe(expected)
-    expect(get).toHaveBeenCalledWith(key)
-    const setArgs = set.mock.calls[0]
-    expect(setArgs[0]).toBe(key)
-    expect(setArgs[1].html).toBe(expected)
-    expect(cache[key].html).toBe(expected)
-
-    const res2 = await renderer.renderToString()
-    expect(res2).toBe(expected)
-    expect(get.mock.calls.length).toBe(2)
-    expect(set.mock.calls.length).toBe(1)
-  })
-
-  it('render with cache (get/set/has)', async () => {
-    const cache = {}
-    const has = vi.fn()
-    const get = vi.fn()
-    const set = vi.fn()
-    const options = {
-      runInNewContext,
-      cache: {
-        // async
-        has: (key, cb) => {
-          has(key)
-          cb(!!cache[key])
-        },
-        // sync
-        get: key => {
-          get(key)
-          return cache[key]
-        },
-        set: (key, val) => {
-          set(key, val)
-          cache[key] = val
-        }
-      }
-    }
-    const renderer = await createWebpackBundleRenderer('cache.js', options)
-    const expected = '<div data-server-rendered="true">/test</div>'
-    const key = 'app::1'
-    const res = await renderer.renderToString()
-    expect(res).toBe(expected)
-    expect(has).toHaveBeenCalledWith(key)
-    expect(get).not.toHaveBeenCalled()
-    const setArgs = set.mock.calls[0]
-    expect(setArgs[0]).toBe(key)
-    expect(setArgs[1].html).toBe(expected)
-    expect(cache[key].html).toBe(expected)
-
-    const res2 = await renderer.renderToString()
-    expect(res2).toBe(expected)
-    expect(has.mock.calls.length).toBe(2)
-    expect(get.mock.calls.length).toBe(1)
-    expect(set.mock.calls.length).toBe(1)
-  })
-
-  it('render with cache (nested)', async () => {
-    const cache = new Map() as any
-    vi.spyOn(cache, 'get')
-    vi.spyOn(cache, 'set')
-    const options = {
-      cache,
-      runInNewContext
-    }
-    const renderer = await createWebpackBundleRenderer(
-      'nested-cache.js',
-      options
-    )
-    const expected = '<div data-server-rendered="true">/test</div>'
-    const key = 'app::1'
-    const context1 = { registered: [] }
-    const context2 = { registered: [] }
-    const res = await renderer.renderToString(context1)
-    expect(res).toBe(expected)
-    expect(cache.set.mock.calls.length).toBe(3) // 3 nested components cached
-    const cached = cache.get(key)
-    expect(cached.html).toBe(expected)
-    expect(cache.get.mock.calls.length).toBe(1)
-
-    // assert component usage registration for nested children
-    expect(context1.registered).toEqual(['app', 'child', 'grandchild'])
-
-    const res2 = await renderer.renderToString(context2)
-    expect(res2).toBe(expected)
-    expect(cache.set.mock.calls.length).toBe(3) // no new cache sets
-    expect(cache.get.mock.calls.length).toBe(2) // 1 get for root
-
-    expect(context2.registered).toEqual(['app', 'child', 'grandchild'])
-  })
-
-  it('render with cache (opt-out)', async () => {
-    const cache = {}
-    const get = vi.fn()
-    const set = vi.fn()
-    const options = {
-      runInNewContext,
-      cache: {
-        // async
-        get: (key, cb) => {
-          setTimeout(() => {
-            get(key)
-            cb(cache[key])
-          }, 0)
-        },
-        set: (key, val) => {
-          set(key, val)
-          cache[key] = val
-        }
-      }
-    }
-    const renderer = await createWebpackBundleRenderer(
-      'cache-opt-out.js',
-      options
-    )
-    const expected = '<div data-server-rendered="true">/test</div>'
-    const res = await renderer.renderToString()
-    expect(res).toBe(expected)
-    expect(get).not.toHaveBeenCalled()
-    expect(set).not.toHaveBeenCalled()
-    const res2 = await renderer.renderToString()
-    expect(res2).toBe(expected)
-    expect(get).not.toHaveBeenCalled()
-    expect(set).not.toHaveBeenCalled()
-  })
-
-  it('renderToString (bundle format with code split)', async () => {
-    const renderer = await createWebpackBundleRenderer('split.js', {
-      runInNewContext,
-      asBundle: true
-    })
-    const context = { url: '/test' }
-    const res = await renderer.renderToString(context)
-    expect(res).toBe(
-      '<div data-server-rendered="true">/test<div>async test.woff2 test.png</div></div>'
-    )
-  })
-
-  it('renderToStream (bundle format with code split)', async () => {
-    const renderer = await createWebpackBundleRenderer('split.js', {
-      runInNewContext,
-      asBundle: true
-    })
-    const context = { url: '/test' }
-
-    const res = await new Promise((resolve, reject) => {
-      const stream = renderer.renderToStream(context)
-      let res = ''
-      stream.on('data', chunk => {
-        res += chunk.toString()
-      })
-      stream.on('error', reject)
-      stream.on('end', () => {
-        resolve(res)
-      })
-    })
-
-    expect(res).toBe(
-      '<div data-server-rendered="true">/test<div>async test.woff2 test.png</div></div>'
-    )
-  })
-
-  it('renderToString catch error (bundle format with source map)', async () => {
-    const renderer = await createWebpackBundleRenderer('error.js', {
-      runInNewContext,
-      asBundle: true
-    })
-
-    try {
-      await renderer.renderToString()
-    } catch (err: any) {
-      expect(err.stack).toContain('server-renderer/test/fixtures/error.js:1:0')
-      expect(err.message).toBe('foo')
-    }
-  })
-
-  it('renderToStream catch error (bundle format with source map)', async () => {
-    const renderer = await createWebpackBundleRenderer('error.js', {
-      runInNewContext,
-      asBundle: true
-    })
-
-    const err = await new Promise<Error>(resolve => {
-      const stream = renderer.renderToStream()
-      stream.on('error', resolve)
-    })
-
-    expect(err.stack).toContain('server-renderer/test/fixtures/error.js:1:0')
-    expect(err.message).toBe('foo')
-  })
-
-  it('renderToString w/ callback', async () => {
-    const renderer = await createWebpackBundleRenderer('app.js', {
-      runInNewContext
-    })
-    const context: any = { url: '/test' }
-    const res = await new Promise(r =>
-      renderer.renderToString(context, (_err, res) => r(res))
-    )
-    expect(res).toBe('<div data-server-rendered="true">/test</div>')
-    expect(context.msg).toBe('hello')
-  })
-
-  it('renderToString error handling w/ callback', async () => {
-    const renderer = await createWebpackBundleRenderer('error.js', {
-      runInNewContext
-    })
-    const err = await new Promise<Error>(r => renderer.renderToString(r))
-    expect(err.message).toBe('foo')
-  })
-}
diff --git a/packages/server-renderer/test/ssr-reactivity.spec.ts b/packages/server-renderer/test/ssr-reactivity.spec.ts
deleted file mode 100644
index 773fa9dd650..00000000000
--- a/packages/server-renderer/test/ssr-reactivity.spec.ts
+++ /dev/null
@@ -1,196 +0,0 @@
-// @vitest-environment node
-
-import Vue from 'vue'
-import {
-  reactive,
-  ref,
-  isReactive,
-  shallowRef,
-  isRef,
-  set,
-  nextTick,
-  getCurrentInstance
-} from 'v3'
-import { createRenderer } from '../src'
-
-describe('SSR Reactive', () => {
-  beforeEach(() => {
-    // force SSR env
-    global.process.env.VUE_ENV = 'server'
-  })
-
-  it('should not affect non reactive APIs', () => {
-    expect(typeof window).toBe('undefined')
-    expect((Vue.observable({}) as any).__ob__).toBeUndefined()
-  })
-
-  it('reactive behavior should be consistent in SSR', () => {
-    const obj = reactive({
-      foo: ref(1),
-      bar: {
-        baz: ref(2)
-      },
-      arr: [{ foo: ref(3) }]
-    })
-    expect(isReactive(obj)).toBe(true)
-    expect(obj.foo).toBe(1)
-
-    expect(isReactive(obj.bar)).toBe(true)
-    expect(obj.bar.baz).toBe(2)
-
-    expect(isReactive(obj.arr)).toBe(true)
-    expect(isReactive(obj.arr[0])).toBe(true)
-    expect(obj.arr[0].foo).toBe(3)
-  })
-
-  it('ref value', () => {
-    const r = ref({})
-    expect(isReactive(r.value)).toBe(true)
-  })
-
-  it('should render', async () => {
-    const app = new Vue({
-      setup() {
-        return {
-          count: ref(42)
-        }
-      },
-      render(this: any, h) {
-        return h('div', this.count)
-      }
-    })
-
-    const serverRenderer = createRenderer()
-    const html = await serverRenderer.renderToString(app)
-    expect(html).toBe('<div data-server-rendered="true">42</div>')
-  })
-
-  it('reactive + isReactive', () => {
-    const state = reactive({})
-    expect(isReactive(state)).toBe(true)
-  })
-
-  it('shallowRef + isRef', () => {
-    const state = shallowRef({})
-    expect(isRef(state)).toBe(true)
-  })
-
-  it('should work on objects sets with set()', () => {
-    const state = ref<any>({})
-
-    set(state.value, 'a', {})
-    expect(isReactive(state.value.a)).toBe(true)
-
-    set(state.value, 'a', {})
-    expect(isReactive(state.value.a)).toBe(true)
-  })
-
-  it('should work on arrays sets with set()', () => {
-    const state = ref<any>([])
-
-    set(state.value, 1, {})
-    expect(isReactive(state.value[1])).toBe(true)
-
-    set(state.value, 1, {})
-    expect(isReactive(state.value[1])).toBe(true)
-
-    const rawArr = []
-    set(rawArr, 1, {})
-    expect(isReactive(rawArr[1])).toBe(false)
-  })
-
-  // #550
-  it('props should work with set', async done => {
-    let props: any
-
-    const app = new Vue({
-      render(this: any, h) {
-        return h('child', { attrs: { msg: this.msg } })
-      },
-      setup() {
-        return { msg: ref('hello') }
-      },
-      components: {
-        child: {
-          render(this: any, h: any) {
-            return h('span', this.data.msg)
-          },
-          props: ['msg'],
-          setup(_props) {
-            props = _props
-
-            return { data: _props }
-          }
-        }
-      }
-    })
-
-    const serverRenderer = createRenderer()
-    const html = await serverRenderer.renderToString(app)
-
-    expect(html).toBe('<span data-server-rendered="true">hello</span>')
-
-    expect(props.bar).toBeUndefined()
-    set(props, 'bar', 'bar')
-    expect(props.bar).toBe('bar')
-
-    done()
-  })
-
-  // #721
-  it('should behave correctly', () => {
-    const state = ref({ old: ref(false) })
-    set(state.value, 'new', ref(true))
-    // console.log(process.server, 'state.value', JSON.stringify(state.value))
-
-    expect(state.value).toMatchObject({
-      old: false,
-      new: true
-    })
-  })
-
-  // #721
-  it('should behave correctly for the nested ref in the object', () => {
-    const state = { old: ref(false) }
-    set(state, 'new', ref(true))
-    expect(JSON.stringify(state)).toBe(
-      '{"old":{"value":false},"new":{"value":true}}'
-    )
-  })
-
-  // #721
-  it('should behave correctly for ref of object', () => {
-    const state = ref({ old: ref(false) })
-    set(state.value, 'new', ref(true))
-    expect(JSON.stringify(state.value)).toBe('{"old":false,"new":true}')
-  })
-
-  it('ssr should not RangeError: Maximum call stack size exceeded', async () => {
-    new Vue({
-      setup() {
-        // @ts-expect-error
-        const app = getCurrentInstance().proxy
-        let mockNt: any = []
-        mockNt.__ob__ = {}
-        const test = reactive({
-          app,
-          mockNt
-        })
-        return {
-          test
-        }
-      }
-    })
-    await nextTick()
-    expect(
-      `"RangeError: Maximum call stack size exceeded"`
-    ).not.toHaveBeenWarned()
-  })
-
-  it('should work on objects sets with set()', () => {
-    const state = ref<any>({})
-    set(state.value, 'a', {})
-
-    expect(isReactive(state.value.a)).toBe(true)
-  })
-})
diff --git a/packages/server-renderer/test/ssr-stream.spec.ts b/packages/server-renderer/test/ssr-stream.spec.ts
deleted file mode 100644
index 638befa44b5..00000000000
--- a/packages/server-renderer/test/ssr-stream.spec.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-// @vitest-environment node
-
-import Vue from 'vue'
-import { createRenderer } from 'server/index'
-import { _it } from './utils'
-
-const { renderToStream } = createRenderer()
-
-describe('SSR: renderToStream', () => {
-  _it('should render to a stream', done => {
-    const stream = renderToStream(
-      new Vue({
-        template: `
-        <div>
-          <p class="hi">yoyo</p>
-          <div id="ho" :class="[testClass, { red: isRed }]"></div>
-          <span>{{ test }}</span>
-          <input :value="test">
-          <b-comp></b-comp>
-          <c-comp></c-comp>
-        </div>
-      `,
-        data: {
-          test: 'hi',
-          isRed: true,
-          testClass: 'a'
-        },
-        components: {
-          bComp(resolve) {
-            return resolve({
-              render(h) {
-                return h('test-async-2')
-              },
-              components: {
-                testAsync2(resolve) {
-                  return resolve({
-                    created() {
-                      this.$parent.$parent.testClass = 'b'
-                    },
-                    render(h) {
-                      return h(
-                        'div',
-                        { class: [this.$parent.$parent.testClass] },
-                        'test'
-                      )
-                    }
-                  })
-                }
-              }
-            })
-          },
-          cComp: {
-            render(h) {
-              return h('div', { class: [this.$parent.testClass] }, 'test')
-            }
-          }
-        }
-      })
-    )
-    let res = ''
-    stream.on('data', chunk => {
-      res += chunk
-    })
-    stream.on('end', () => {
-      expect(res).toContain(
-        '<div data-server-rendered="true">' +
-          '<p class="hi">yoyo</p> ' +
-          '<div id="ho" class="a red"></div> ' +
-          '<span>hi</span> ' +
-          '<input value="hi"> ' +
-          '<div class="b">test</div> ' +
-          '<div class="b">test</div>' +
-          '</div>'
-      )
-      done()
-    })
-  })
-
-  _it('should catch error', done => {
-    const stream = renderToStream(
-      new Vue({
-        render() {
-          throw new Error('oops')
-        }
-      })
-    )
-    stream.on('error', err => {
-      expect(err.toString()).toMatch(/oops/)
-      expect(`oops`).toHaveBeenWarned()
-      done()
-    })
-    stream.on('data', _ => _)
-  })
-
-  _it('should not mingle two components', done => {
-    const padding = new Array(20000).join('x')
-    const component1 = new Vue({
-      template: `<div>${padding}<div></div></div>`,
-      _scopeId: '_component1'
-    })
-    const component2 = new Vue({
-      template: `<div></div>`,
-      _scopeId: '_component2'
-    })
-    const stream1 = renderToStream(component1)
-    const stream2 = renderToStream(component2)
-    let res = ''
-    stream1.on('data', text => {
-      res += text.toString('utf-8').replace(/x/g, '')
-    })
-    stream1.on('end', () => {
-      expect(res).not.toContain('_component2')
-      done()
-    })
-    stream1.read(1)
-    stream2.read(1)
-  })
-
-  _it('should call context.rendered', done => {
-    let a = 0
-    const stream = renderToStream(
-      new Vue({
-        template: `
-        <div>Hello</div>
-      `
-      }),
-      {
-        rendered: () => {
-          a = 42
-        }
-      }
-    )
-    let res = ''
-    stream.on('data', chunk => {
-      res += chunk
-    })
-    stream.on('end', () => {
-      expect(res).toContain('<div data-server-rendered="true">Hello</div>')
-      expect(a).toBe(42)
-      done()
-    })
-  })
-})
diff --git a/packages/server-renderer/test/ssr-string.spec.ts b/packages/server-renderer/test/ssr-string.spec.ts
deleted file mode 100644
index e9749c05cca..00000000000
--- a/packages/server-renderer/test/ssr-string.spec.ts
+++ /dev/null
@@ -1,2166 +0,0 @@
-// @vitest-environment node
-
-import Vue from 'vue'
-import VM from 'vm'
-import { createRenderer } from 'server/index'
-import { _it } from './utils'
-
-const { renderToString } = createRenderer()
-
-describe('SSR: renderToString', () => {
-  _it('static attributes', done => {
-    renderVmWithOptions(
-      {
-        template: '<div id="foo" bar="123"></div>'
-      },
-      result => {
-        expect(result).toContain(
-          '<div id="foo" bar="123" data-server-rendered="true"></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('unary tags', done => {
-    renderVmWithOptions(
-      {
-        template: '<input value="123">'
-      },
-      result => {
-        expect(result).toContain(
-          '<input value="123" data-server-rendered="true">'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('dynamic attributes', done => {
-    renderVmWithOptions(
-      {
-        template: '<div qux="quux" :id="foo" :bar="baz"></div>',
-        data: {
-          foo: 'hi',
-          baz: 123
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div qux="quux" id="hi" bar="123" data-server-rendered="true"></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('static class', done => {
-    renderVmWithOptions(
-      {
-        template: '<div class="foo bar"></div>'
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" class="foo bar"></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('dynamic class', done => {
-    renderVmWithOptions(
-      {
-        template:
-          '<div class="foo bar" :class="[a, { qux: hasQux, quux: hasQuux }]"></div>',
-        data: {
-          a: 'baz',
-          hasQux: true,
-          hasQuux: false
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" class="foo bar baz qux"></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('custom component class', done => {
-    renderVmWithOptions(
-      {
-        template: '<div><cmp class="cmp"></cmp></div>',
-        components: {
-          cmp: {
-            render: h => h('div', 'test')
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><div class="cmp">test</div></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('nested component class', done => {
-    renderVmWithOptions(
-      {
-        template: '<cmp class="outer" :class="cls"></cmp>',
-        data: { cls: { success: 1 } },
-        components: {
-          cmp: {
-            render: h =>
-              h('div', [
-                h('nested', { staticClass: 'nested', class: { error: 1 } })
-              ]),
-            components: {
-              nested: {
-                render: h => h('div', { staticClass: 'inner' }, 'test')
-              }
-            }
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" class="outer success">' +
-            '<div class="inner nested error">test</div>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('dynamic style', done => {
-    renderVmWithOptions(
-      {
-        template:
-          '<div style="background-color:black" :style="{ fontSize: fontSize + \'px\', color: color }"></div>',
-        data: {
-          fontSize: 14,
-          color: 'red'
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" style="background-color:black;font-size:14px;color:red;"></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('dynamic string style', done => {
-    renderVmWithOptions(
-      {
-        template: '<div :style="style"></div>',
-        data: {
-          style: 'color:red'
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" style="color:red;"></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('auto-prefixed style value as array', done => {
-    renderVmWithOptions(
-      {
-        template: '<div :style="style"></div>',
-        data: {
-          style: {
-            display: ['-webkit-box', '-ms-flexbox', 'flex']
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" style="display:-webkit-box;display:-ms-flexbox;display:flex;"></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('custom component style', done => {
-    renderVmWithOptions(
-      {
-        template: '<section><comp :style="style"></comp></section>',
-        data: {
-          style: 'color:red'
-        },
-        components: {
-          comp: {
-            template: '<div></div>'
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<section data-server-rendered="true"><div style="color:red;"></div></section>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('nested custom component style', done => {
-    renderVmWithOptions(
-      {
-        template: '<comp style="color: blue" :style="style"></comp>',
-        data: {
-          style: 'color:red'
-        },
-        components: {
-          comp: {
-            template:
-              '<nested style="text-align: left;" :style="{fontSize:\'520rem\'}"></nested>',
-            components: {
-              nested: {
-                template: '<div></div>'
-              }
-            }
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" style="text-align:left;font-size:520rem;color:red;"></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('component style not passed to child', done => {
-    renderVmWithOptions(
-      {
-        template: '<comp :style="style"></comp>',
-        data: {
-          style: 'color:red'
-        },
-        components: {
-          comp: {
-            template: '<div><div></div></div>'
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" style="color:red;"><div></div></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('component style not passed to slot', done => {
-    renderVmWithOptions(
-      {
-        template:
-          '<comp :style="style"><span style="color:black"></span></comp>',
-        data: {
-          style: 'color:red'
-        },
-        components: {
-          comp: {
-            template: '<div><slot></slot></div>'
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" style="color:red;"><span style="color:black;"></span></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('attrs merging on components', done => {
-    const Test = {
-      render: h =>
-        h('div', {
-          attrs: { id: 'a' }
-        })
-    }
-    renderVmWithOptions(
-      {
-        render: h =>
-          h(Test, {
-            attrs: { id: 'b', name: 'c' }
-          })
-      },
-      res => {
-        expect(res).toContain(
-          '<div id="b" data-server-rendered="true" name="c"></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('domProps merging on components', done => {
-    const Test = {
-      render: h =>
-        h('div', {
-          domProps: { innerHTML: 'a' }
-        })
-    }
-    renderVmWithOptions(
-      {
-        render: h =>
-          h(Test, {
-            domProps: { innerHTML: 'b', value: 'c' }
-          })
-      },
-      res => {
-        expect(res).toContain(
-          '<div data-server-rendered="true" value="c">b</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-show directive render', done => {
-    renderVmWithOptions(
-      {
-        template: '<div v-show="false"><span>inner</span></div>'
-      },
-      res => {
-        expect(res).toContain(
-          '<div data-server-rendered="true" style="display:none;"><span>inner</span></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-show directive merge with style', done => {
-    renderVmWithOptions(
-      {
-        template:
-          '<div :style="[{lineHeight: 1}]" v-show="false"><span>inner</span></div>'
-      },
-      res => {
-        expect(res).toContain(
-          '<div data-server-rendered="true" style="line-height:1;display:none;"><span>inner</span></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-show directive not passed to child', done => {
-    renderVmWithOptions(
-      {
-        template: '<foo v-show="false"></foo>',
-        components: {
-          foo: {
-            template: '<div><span>inner</span></div>'
-          }
-        }
-      },
-      res => {
-        expect(res).toContain(
-          '<div data-server-rendered="true" style="display:none;"><span>inner</span></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-show directive not passed to slot', done => {
-    renderVmWithOptions(
-      {
-        template: '<foo v-show="false"><span>inner</span></foo>',
-        components: {
-          foo: {
-            template: '<div><slot></slot></div>'
-          }
-        }
-      },
-      res => {
-        expect(res).toContain(
-          '<div data-server-rendered="true" style="display:none;"><span>inner</span></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-show directive merging on components', done => {
-    renderVmWithOptions(
-      {
-        template: '<foo v-show="false"></foo>',
-        components: {
-          foo: {
-            render: h =>
-              h('bar', {
-                directives: [
-                  {
-                    name: 'show',
-                    value: true
-                  }
-                ]
-              }),
-            components: {
-              bar: {
-                render: h => h('div', 'inner')
-              }
-            }
-          }
-        }
-      },
-      res => {
-        expect(res).toContain(
-          '<div data-server-rendered="true" style="display:none;">inner</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('text interpolation', done => {
-    renderVmWithOptions(
-      {
-        template: '<div>{{ foo }} side {{ bar }}</div>',
-        data: {
-          foo: 'server',
-          bar: '<span>rendering</span>'
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true">server side &lt;span&gt;rendering&lt;/span&gt;</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-html on root', done => {
-    renderVmWithOptions(
-      {
-        template: '<div v-html="text"></div>',
-        data: {
-          text: '<span>foo</span>'
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><span>foo</span></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-text on root', done => {
-    renderVmWithOptions(
-      {
-        template: '<div v-text="text"></div>',
-        data: {
-          text: '<span>foo</span>'
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true">&lt;span&gt;foo&lt;/span&gt;</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-html', done => {
-    renderVmWithOptions(
-      {
-        template: '<div><div v-html="text"></div></div>',
-        data: {
-          text: '<span>foo</span>'
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><div><span>foo</span></div></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-html with null value', done => {
-    renderVmWithOptions(
-      {
-        template: '<div><div v-html="text"></div></div>',
-        data: {
-          text: null
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><div></div></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-text', done => {
-    renderVmWithOptions(
-      {
-        template: '<div><div v-text="text"></div></div>',
-        data: {
-          text: '<span>foo</span>'
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><div>&lt;span&gt;foo&lt;/span&gt;</div></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-text with null value', done => {
-    renderVmWithOptions(
-      {
-        template: '<div><div v-text="text"></div></div>',
-        data: {
-          text: null
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><div></div></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('child component (hoc)', done => {
-    renderVmWithOptions(
-      {
-        template: '<child class="foo" :msg="msg"></child>',
-        data: {
-          msg: 'hello'
-        },
-        components: {
-          child: {
-            props: ['msg'],
-            data() {
-              return { name: 'bar' }
-            },
-            render() {
-              const h = this.$createElement
-              return h('div', { class: ['bar'] }, [`${this.msg} ${this.name}`])
-            }
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" class="foo bar">hello bar</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('has correct lifecycle during render', done => {
-    let lifecycleCount = 1
-    renderVmWithOptions(
-      {
-        template: '<div><span>{{ val }}</span><test></test></div>',
-        data: {
-          val: 'hi'
-        },
-        beforeCreate() {
-          expect(lifecycleCount++).toBe(1)
-        },
-        created() {
-          this.val = 'hello'
-          expect(this.val).toBe('hello')
-          expect(lifecycleCount++).toBe(2)
-        },
-        components: {
-          test: {
-            beforeCreate() {
-              expect(lifecycleCount++).toBe(3)
-            },
-            created() {
-              expect(lifecycleCount++).toBe(4)
-            },
-            render() {
-              expect(lifecycleCount++).toBeGreaterThan(4)
-              return this.$createElement('span', { class: ['b'] }, 'testAsync')
-            }
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true">' +
-            '<span>hello</span>' +
-            '<span class="b">testAsync</span>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('computed properties', done => {
-    renderVmWithOptions(
-      {
-        template: '<div>{{ b }}</div>',
-        data: {
-          a: {
-            b: 1
-          }
-        },
-        computed: {
-          b() {
-            return this.a.b + 1
-          }
-        },
-        created() {
-          this.a.b = 2
-          expect(this.b).toBe(3)
-        }
-      },
-      result => {
-        expect(result).toContain('<div data-server-rendered="true">3</div>')
-        done()
-      }
-    )
-  })
-
-  _it('renders async component', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <test-async></test-async>
-        </div>
-      `,
-        components: {
-          testAsync(resolve) {
-            setTimeout(
-              () =>
-                resolve({
-                  render() {
-                    return this.$createElement(
-                      'span',
-                      { class: ['b'] },
-                      'testAsync'
-                    )
-                  }
-                }),
-              1
-            )
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><span class="b">testAsync</span></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('renders async component (Promise, nested)', done => {
-    const Foo = () =>
-      Promise.resolve({
-        render: h => h('div', [h('span', 'foo'), h(Bar)])
-      })
-    const Bar = () => ({
-      component: Promise.resolve({
-        render: h => h('span', 'bar')
-      })
-    })
-    renderVmWithOptions(
-      {
-        render: h => h(Foo)
-      },
-      res => {
-        expect(res).toContain(
-          `<div data-server-rendered="true"><span>foo</span><span>bar</span></div>`
-        )
-        done()
-      }
-    )
-  })
-
-  _it('renders async component (ES module)', done => {
-    const Foo = () =>
-      Promise.resolve({
-        __esModule: true,
-        default: {
-          render: h => h('div', [h('span', 'foo'), h(Bar)])
-        }
-      })
-    const Bar = () => ({
-      component: Promise.resolve({
-        __esModule: true,
-        default: {
-          render: h => h('span', 'bar')
-        }
-      })
-    })
-    renderVmWithOptions(
-      {
-        render: h => h(Foo)
-      },
-      res => {
-        expect(res).toContain(
-          `<div data-server-rendered="true"><span>foo</span><span>bar</span></div>`
-        )
-        done()
-      }
-    )
-  })
-
-  _it('renders async component (hoc)', done => {
-    renderVmWithOptions(
-      {
-        template: '<test-async></test-async>',
-        components: {
-          testAsync: () =>
-            Promise.resolve({
-              render() {
-                return this.$createElement(
-                  'span',
-                  { class: ['b'] },
-                  'testAsync'
-                )
-              }
-            })
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<span data-server-rendered="true" class="b">testAsync</span>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('renders async component (functional, single node)', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <test-async></test-async>
-        </div>
-      `,
-        components: {
-          testAsync(resolve) {
-            setTimeout(
-              () =>
-                resolve({
-                  functional: true,
-                  render(h) {
-                    return h('span', { class: ['b'] }, 'testAsync')
-                  }
-                }),
-              1
-            )
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><span class="b">testAsync</span></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('renders async component (functional, multiple nodes)', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <test-async></test-async>
-        </div>
-      `,
-        components: {
-          testAsync(resolve) {
-            setTimeout(
-              () =>
-                resolve({
-                  functional: true,
-                  render(h) {
-                    return [
-                      h('span', { class: ['a'] }, 'foo'),
-                      h('span', { class: ['b'] }, 'bar')
-                    ]
-                  }
-                }),
-              1
-            )
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true">' +
-            '<span class="a">foo</span>' +
-            '<span class="b">bar</span>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('renders nested async functional component', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <outer-async></outer-async>
-        </div>
-      `,
-        components: {
-          outerAsync(resolve) {
-            setTimeout(
-              () =>
-                resolve({
-                  functional: true,
-                  render(h) {
-                    return h('innerAsync')
-                  }
-                }),
-              1
-            )
-          },
-          innerAsync(resolve) {
-            setTimeout(
-              () =>
-                resolve({
-                  functional: true,
-                  render(h) {
-                    return h('span', { class: ['a'] }, 'inner')
-                  }
-                }),
-              1
-            )
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true">' +
-            '<span class="a">inner</span>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('should catch async component error', done => {
-    renderToString(
-      new Vue({
-        template: '<test-async></test-async>',
-        components: {
-          testAsync: () =>
-            Promise.resolve({
-              render() {
-                throw new Error('foo')
-              }
-            })
-        }
-      }),
-      (err, result) => {
-        expect(err).toBeTruthy()
-        expect(result).toBeUndefined()
-        expect('foo').toHaveBeenWarned()
-        done()
-      }
-    )
-  })
-
-  // #11963, #10391
-  _it('renders async children passed in slots', done => {
-    const Parent = {
-      template: `<div><slot name="child"/></div>`
-    }
-    const Child = {
-      template: `<p>child</p>`
-    }
-    renderVmWithOptions(
-      {
-        template: `
-      <Parent>
-        <template #child>
-          <Child/>
-        </template>
-      </Parent>
-      `,
-        components: {
-          Parent,
-          Child: () => Promise.resolve(Child)
-        }
-      },
-      result => {
-        expect(result).toContain(
-          `<div data-server-rendered="true"><p>child</p></div>`
-        )
-        done()
-      }
-    )
-  })
-
-  _it('everything together', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <p class="hi">yoyo</p>
-          <div id="ho" :class="{ red: isRed }"></div>
-          <span>{{ test }}</span>
-          <input :value="test">
-          <img :src="imageUrl">
-          <test></test>
-          <test-async></test-async>
-        </div>
-      `,
-        data: {
-          test: 'hi',
-          isRed: true,
-          imageUrl: 'https://vuejs.org/images/logo.png'
-        },
-        components: {
-          test: {
-            render() {
-              return this.$createElement('div', { class: ['a'] }, 'test')
-            }
-          },
-          testAsync(resolve) {
-            resolve({
-              render() {
-                return this.$createElement(
-                  'span',
-                  { class: ['b'] },
-                  'testAsync'
-                )
-              }
-            })
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true">' +
-            '<p class="hi">yoyo</p> ' +
-            '<div id="ho" class="red"></div> ' +
-            '<span>hi</span> ' +
-            '<input value="hi"> ' +
-            '<img src="https://vuejs.org/images/logo.png"> ' +
-            '<div class="a">test</div> ' +
-            '<span class="b">testAsync</span>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('normal attr', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <span :test="'ok'">hello</span>
-          <span :test="null">hello</span>
-          <span :test="false">hello</span>
-          <span :test="true">hello</span>
-          <span :test="0">hello</span>
-        </div>
-      `
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true">' +
-            '<span test="ok">hello</span> ' +
-            '<span>hello</span> ' +
-            '<span>hello</span> ' +
-            '<span test="true">hello</span> ' +
-            '<span test="0">hello</span>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('enumerated attr', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <span :draggable="true">hello</span>
-          <span :draggable="'ok'">hello</span>
-          <span :draggable="null">hello</span>
-          <span :draggable="false">hello</span>
-          <span :draggable="''">hello</span>
-          <span :draggable="'false'">hello</span>
-        </div>
-      `
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true">' +
-            '<span draggable="true">hello</span> ' +
-            '<span draggable="true">hello</span> ' +
-            '<span draggable="false">hello</span> ' +
-            '<span draggable="false">hello</span> ' +
-            '<span draggable="true">hello</span> ' +
-            '<span draggable="false">hello</span>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('boolean attr', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <span :disabled="true">hello</span>
-          <span :disabled="'ok'">hello</span>
-          <span :disabled="null">hello</span>
-          <span :disabled="''">hello</span>
-        </div>
-      `
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true">' +
-            '<span disabled="disabled">hello</span> ' +
-            '<span disabled="disabled">hello</span> ' +
-            '<span>hello</span> ' +
-            '<span disabled="disabled">hello</span>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-bind object', done => {
-    renderVmWithOptions(
-      {
-        data: {
-          test: { id: 'a', class: ['a', 'b'], value: 'c' }
-        },
-        template: '<input v-bind="test">'
-      },
-      result => {
-        expect(result).toContain(
-          '<input id="a" data-server-rendered="true" value="c" class="a b">'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('custom directives on raw element', done => {
-    const renderer = createRenderer({
-      directives: {
-        'class-prefixer': (node, dir) => {
-          if (node.data.class) {
-            node.data.class = `${dir.value}-${node.data.class}`
-          }
-          if (node.data.staticClass) {
-            node.data.staticClass = `${dir.value}-${node.data.staticClass}`
-          }
-        }
-      }
-    })
-    renderer.renderToString(
-      new Vue({
-        render() {
-          const h = this.$createElement
-          return h(
-            'p',
-            {
-              class: 'class1',
-              staticClass: 'class2',
-              directives: [
-                {
-                  name: 'class-prefixer',
-                  value: 'my'
-                }
-              ]
-            },
-            ['hello world']
-          )
-        }
-      }),
-      (err, result) => {
-        expect(err).toBeNull()
-        expect(result).toContain(
-          '<p data-server-rendered="true" class="my-class2 my-class1">hello world</p>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('custom directives on component', done => {
-    const Test = {
-      template: '<span>hello world</span>'
-    }
-    const renderer = createRenderer({
-      directives: {
-        'class-prefixer': (node, dir) => {
-          if (node.data.class) {
-            node.data.class = `${dir.value}-${node.data.class}`
-          }
-          if (node.data.staticClass) {
-            node.data.staticClass = `${dir.value}-${node.data.staticClass}`
-          }
-        }
-      }
-    })
-    renderer.renderToString(
-      new Vue({
-        template:
-          '<p><Test v-class-prefixer="\'my\'" class="class1" :class="\'class2\'" /></p>',
-        components: { Test }
-      }),
-      (err, result) => {
-        expect(err).toBeNull()
-        expect(result).toContain(
-          '<p data-server-rendered="true"><span class="my-class1 my-class2">hello world</span></p>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('custom directives on element root of a component', done => {
-    const Test = {
-      template:
-        '<span v-class-prefixer="\'my\'" class="class1" :class="\'class2\'">hello world</span>'
-    }
-    const renderer = createRenderer({
-      directives: {
-        'class-prefixer': (node, dir) => {
-          if (node.data.class) {
-            node.data.class = `${dir.value}-${node.data.class}`
-          }
-          if (node.data.staticClass) {
-            node.data.staticClass = `${dir.value}-${node.data.staticClass}`
-          }
-        }
-      }
-    })
-    renderer.renderToString(
-      new Vue({
-        template: '<p><Test /></p>',
-        components: { Test }
-      }),
-      (err, result) => {
-        expect(err).toBeNull()
-        expect(result).toContain(
-          '<p data-server-rendered="true"><span class="my-class1 my-class2">hello world</span></p>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('custom directives on element with parent element', done => {
-    const renderer = createRenderer({
-      directives: {
-        'class-prefixer': (node, dir) => {
-          if (node.data.class) {
-            node.data.class = `${dir.value}-${node.data.class}`
-          }
-          if (node.data.staticClass) {
-            node.data.staticClass = `${dir.value}-${node.data.staticClass}`
-          }
-        }
-      }
-    })
-    renderer.renderToString(
-      new Vue({
-        template:
-          '<p><span v-class-prefixer="\'my\'" class="class1" :class="\'class2\'">hello world</span></p>'
-      }),
-      (err, result) => {
-        expect(err).toBeNull()
-        expect(result).toContain(
-          '<p data-server-rendered="true"><span class="my-class1 my-class2">hello world</span></p>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it(
-    'should not warn for custom directives that do not have server-side implementation',
-    done => {
-      renderToString(
-        new Vue({
-          directives: {
-            test: {
-              bind() {
-                // noop
-              }
-            }
-          },
-          template: '<div v-test></div>'
-        }),
-        () => {
-          expect('Failed to resolve directive: test').not.toHaveBeenWarned()
-          done()
-        }
-      )
-    }
-  )
-
-  _it('_scopeId', done => {
-    renderVmWithOptions(
-      {
-        _scopeId: '_v-parent',
-        template: '<div id="foo"><p><child></child></p></div>',
-        components: {
-          child: {
-            _scopeId: '_v-child',
-            render() {
-              const h = this.$createElement
-              return h('div', null, [h('span', null, ['foo'])])
-            }
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div id="foo" data-server-rendered="true" _v-parent>' +
-            '<p _v-parent>' +
-            '<div _v-child _v-parent><span _v-child>foo</span></div>' +
-            '</p>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('_scopeId on slot content', done => {
-    renderVmWithOptions(
-      {
-        _scopeId: '_v-parent',
-        template: '<div><child><p>foo</p></child></div>',
-        components: {
-          child: {
-            _scopeId: '_v-child',
-            render() {
-              const h = this.$createElement
-              return h('div', null, this.$slots.default)
-            }
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" _v-parent>' +
-            '<div _v-child _v-parent><p _v-child _v-parent>foo</p></div>' +
-            '</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('comment nodes', done => {
-    renderVmWithOptions(
-      {
-        template: '<div><transition><div v-if="false"></div></transition></div>'
-      },
-      result => {
-        expect(result).toContain(
-          `<div data-server-rendered="true"><!----></div>`
-        )
-        done()
-      }
-    )
-  })
-
-  _it('should catch error', done => {
-    renderToString(
-      new Vue({
-        render() {
-          throw new Error('oops')
-        }
-      }),
-      err => {
-        expect(err instanceof Error).toBe(true)
-        expect(`oops`).toHaveBeenWarned()
-        done()
-      }
-    )
-  })
-
-  _it('default value Foreign Function', () => {
-    const FunctionConstructor = VM.runInNewContext('Function')
-    const func = () => 123
-    const vm = new Vue({
-      props: {
-        a: {
-          type: FunctionConstructor,
-          default: func
-        }
-      },
-      propsData: {
-        a: undefined
-      }
-    })
-    expect(vm.a).toBe(func)
-  })
-
-  _it('should prevent xss in attributes', done => {
-    renderVmWithOptions(
-      {
-        data: {
-          xss: '"><script>alert(1)</script>'
-        },
-        template: `
-        <div>
-          <a :title="xss" :style="{ color: xss }" :class="[xss]">foo</a>
-        </div>
-      `
-      },
-      res => {
-        expect(res).not.toContain(`<script>alert(1)</script>`)
-        done()
-      }
-    )
-  })
-
-  _it('should prevent xss in attribute names', done => {
-    renderVmWithOptions(
-      {
-        data: {
-          xss: {
-            'foo="bar"></div><script>alert(1)</script>': ''
-          }
-        },
-        template: `
-        <div v-bind="xss"></div>
-      `
-      },
-      res => {
-        expect(res).not.toContain(`<script>alert(1)</script>`)
-        done()
-      }
-    )
-  })
-
-  _it('should prevent xss in attribute names (optimized)', done => {
-    renderVmWithOptions(
-      {
-        data: {
-          xss: {
-            'foo="bar"></div><script>alert(1)</script>': ''
-          }
-        },
-        template: `
-        <div>
-          <a v-bind="xss">foo</a>
-        </div>
-      `
-      },
-      res => {
-        expect(res).not.toContain(`<script>alert(1)</script>`)
-        done()
-      }
-    )
-  })
-
-  _it(
-    'should prevent script xss with v-bind object syntax + array value',
-    done => {
-      renderVmWithOptions(
-        {
-          data: {
-            test: ['"><script>alert(1)</script><!--"']
-          },
-          template: `<div v-bind="{ test }"></div>`
-        },
-        res => {
-          expect(res).not.toContain(`<script>alert(1)</script>`)
-          done()
-        }
-      )
-    }
-  )
-
-  _it('v-if', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <span v-if="true">foo</span>
-          <span v-if="false">bar</span>
-        </div>
-      `
-      },
-      res => {
-        expect(res).toContain(
-          `<div data-server-rendered="true"><span>foo</span> <!----></div>`
-        )
-        done()
-      }
-    )
-  })
-
-  _it('v-for', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <span>foo</span>
-          <span v-for="i in 2">{{ i }}</span>
-        </div>
-      `
-      },
-      res => {
-        expect(res).toContain(
-          `<div data-server-rendered="true"><span>foo</span> <span>1</span><span>2</span></div>`
-        )
-        done()
-      }
-    )
-  })
-
-  _it('template v-if', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <span>foo</span>
-          <template v-if="true">
-            <span>foo</span> bar <span>baz</span>
-          </template>
-        </div>
-      `
-      },
-      res => {
-        expect(res).toContain(
-          `<div data-server-rendered="true"><span>foo</span> <span>foo</span> bar <span>baz</span></div>`
-        )
-        done()
-      }
-    )
-  })
-
-  _it('template v-for', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <span>foo</span>
-          <template v-for="i in 2">
-            <span>{{ i }}</span><span>bar</span>
-          </template>
-        </div>
-      `
-      },
-      res => {
-        expect(res).toContain(
-          `<div data-server-rendered="true"><span>foo</span> <span>1</span><span>bar</span><span>2</span><span>bar</span></div>`
-        )
-        done()
-      }
-    )
-  })
-
-  _it('with inheritAttrs: false + $attrs', done => {
-    renderVmWithOptions(
-      {
-        template: `<foo id="a"/>`,
-        components: {
-          foo: {
-            inheritAttrs: false,
-            template: `<div><div v-bind="$attrs"></div></div>`
-          }
-        }
-      },
-      res => {
-        expect(res).toBe(
-          `<div data-server-rendered="true"><div id="a"></div></div>`
-        )
-        done()
-      }
-    )
-  })
-
-  _it('should escape static strings', done => {
-    renderVmWithOptions(
-      {
-        template: `<div>&lt;foo&gt;</div>`
-      },
-      res => {
-        expect(res).toBe(`<div data-server-rendered="true">&lt;foo&gt;</div>`)
-        done()
-      }
-    )
-  })
-
-  _it('should not cache computed properties', done => {
-    renderVmWithOptions(
-      {
-        template: `<div>{{ foo }}</div>`,
-        data: () => ({ bar: 1 }),
-        computed: {
-          foo() {
-            return this.bar + 1
-          }
-        },
-        created() {
-          this.foo // access
-          this.bar++ // trigger change
-        }
-      },
-      res => {
-        expect(res).toBe(`<div data-server-rendered="true">3</div>`)
-        done()
-      }
-    )
-  })
-
-  // #8977
-  _it('should call computed properties with vm as first argument', done => {
-    renderToString(
-      new Vue({
-        data: {
-          firstName: 'Evan',
-          lastName: 'You'
-        },
-        computed: {
-          fullName: ({ firstName, lastName }) => `${firstName} ${lastName}`
-        },
-        template: '<div>{{ fullName }}</div>'
-      }),
-      (err, result) => {
-        expect(err).toBeNull()
-        expect(result).toContain(
-          '<div data-server-rendered="true">Evan You</div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('return Promise', async () => {
-    await renderToString(
-      new Vue({
-        template: `<div>{{ foo }}</div>`,
-        data: { foo: 'bar' }
-      })
-    )!.then(res => {
-      expect(res).toBe(`<div data-server-rendered="true">bar</div>`)
-    })
-  })
-
-  _it('return Promise (error)', async () => {
-    await renderToString(
-      new Vue({
-        render() {
-          throw new Error('foobar')
-        }
-      })
-    )!.catch(err => {
-      expect('foobar').toHaveBeenWarned()
-      expect(err.toString()).toContain(`foobar`)
-    })
-  })
-
-  _it('should catch template compilation error', done => {
-    renderToString(
-      new Vue({
-        template: `<div></div><div></div>`
-      }),
-      err => {
-        expect(err.toString()).toContain(
-          'Component template should contain exactly one root element'
-        )
-        done()
-      }
-    )
-  })
-
-  // #6907
-  _it('should not optimize root if conditions', done => {
-    renderVmWithOptions(
-      {
-        data: { foo: 123 },
-        template: `<input :type="'text'" v-model="foo">`
-      },
-      res => {
-        expect(res).toBe(
-          `<input type="text" data-server-rendered="true" value="123">`
-        )
-        done()
-      }
-    )
-  })
-
-  _it('render muted properly', done => {
-    renderVmWithOptions(
-      {
-        template: '<video muted></video>'
-      },
-      result => {
-        expect(result).toContain(
-          '<video muted="muted" data-server-rendered="true"></video>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('render v-model with textarea', done => {
-    renderVmWithOptions(
-      {
-        data: { foo: 'bar' },
-        template: '<div><textarea v-model="foo"></textarea></div>'
-      },
-      result => {
-        expect(result).toContain('<textarea>bar</textarea>')
-        done()
-      }
-    )
-  })
-
-  _it('render v-model with textarea (non-optimized)', done => {
-    renderVmWithOptions(
-      {
-        render(h) {
-          return h('textarea', {
-            domProps: {
-              value: 'foo'
-            }
-          })
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<textarea data-server-rendered="true">foo</textarea>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('render v-model with <select> (value binding)', done => {
-    renderVmWithOptions(
-      {
-        data: {
-          selected: 2,
-          options: [
-            { id: 1, label: 'one' },
-            { id: 2, label: 'two' }
-          ]
-        },
-        template: `
-      <div>
-        <select v-model="selected">
-          <option v-for="o in options" :value="o.id">{{ o.label }}</option>
-        </select>
-      </div>
-      `
-      },
-      result => {
-        expect(result).toContain(
-          '<select>' +
-            '<option value="1">one</option>' +
-            '<option selected="selected" value="2">two</option>' +
-            '</select>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('render v-model with <select> (static value)', done => {
-    renderVmWithOptions(
-      {
-        data: {
-          selected: 2
-        },
-        template: `
-      <div>
-        <select v-model="selected">
-          <option value="1">one</option>
-          <option value="2">two</option>
-        </select>
-      </div>
-      `
-      },
-      result => {
-        expect(result).toContain(
-          '<select>' +
-            '<option value="1">one</option> ' +
-            '<option value="2" selected="selected">two</option>' +
-            '</select>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('render v-model with <select> (text as value)', done => {
-    renderVmWithOptions(
-      {
-        data: {
-          selected: 2,
-          options: [
-            { id: 1, label: 'one' },
-            { id: 2, label: 'two' }
-          ]
-        },
-        template: `
-      <div>
-        <select v-model="selected">
-          <option v-for="o in options">{{ o.id }}</option>
-        </select>
-      </div>
-      `
-      },
-      result => {
-        expect(result).toContain(
-          '<select>' +
-            '<option>1</option>' +
-            '<option selected="selected">2</option>' +
-            '</select>'
-        )
-        done()
-      }
-    )
-  })
-
-  // #7223
-  _it('should not double escape attribute values', done => {
-    renderVmWithOptions(
-      {
-        template: `
-      <div>
-        <div id="a\nb"></div>
-      </div>
-      `
-      },
-      result => {
-        expect(result).toContain(`<div id="a\nb"></div>`)
-        done()
-      }
-    )
-  })
-
-  // #7859
-  _it('should not double escape class values', done => {
-    renderVmWithOptions(
-      {
-        template: `
-      <div>
-        <div class="a\nb"></div>
-      </div>
-      `
-      },
-      result => {
-        expect(result).toContain(`<div class="a b"></div>`)
-        done()
-      }
-    )
-  })
-
-  _it('should expose ssr helpers on functional context', done => {
-    let called = false
-    renderVmWithOptions(
-      {
-        template: `<div><foo/></div>`,
-        components: {
-          foo: {
-            functional: true,
-            render(h, ctx) {
-              expect(ctx._ssrNode).toBeTruthy()
-              called = true
-            }
-          }
-        }
-      },
-      () => {
-        expect(called).toBe(true)
-        done()
-      }
-    )
-  })
-
-  _it('should support serverPrefetch option', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>{{ count }}</div>
-      `,
-        data: {
-          count: 0
-        },
-        serverPrefetch() {
-          return new Promise<void>(resolve => {
-            setTimeout(() => {
-              this.count = 42
-              resolve()
-            }, 1)
-          })
-        }
-      },
-      result => {
-        expect(result).toContain('<div data-server-rendered="true">42</div>')
-        done()
-      }
-    )
-  })
-
-  _it('should support serverPrefetch option (nested)', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <span>{{ count }}</span>
-          <nested-prefetch></nested-prefetch>
-        </div>
-      `,
-        data: {
-          count: 0
-        },
-        serverPrefetch() {
-          return new Promise<void>(resolve => {
-            setTimeout(() => {
-              this.count = 42
-              resolve()
-            }, 1)
-          })
-        },
-        components: {
-          nestedPrefetch: {
-            template: `
-            <div>{{ message }}</div>
-          `,
-            data() {
-              return {
-                message: ''
-              }
-            },
-            serverPrefetch() {
-              return new Promise<void>(resolve => {
-                setTimeout(() => {
-                  this.message = 'vue.js'
-                  resolve()
-                }, 1)
-              })
-            }
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><span>42</span> <div>vue.js</div></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('should support serverPrefetch option (nested async)', done => {
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <span>{{ count }}</span>
-          <nested-prefetch></nested-prefetch>
-        </div>
-      `,
-        data: {
-          count: 0
-        },
-        serverPrefetch() {
-          return new Promise<void>(resolve => {
-            setTimeout(() => {
-              this.count = 42
-              resolve()
-            }, 1)
-          })
-        },
-        components: {
-          nestedPrefetch(resolve) {
-            resolve({
-              template: `
-              <div>{{ message }}</div>
-            `,
-              data() {
-                return {
-                  message: ''
-                }
-              },
-              serverPrefetch() {
-                return new Promise<void>(resolve => {
-                  setTimeout(() => {
-                    this.message = 'vue.js'
-                    resolve()
-                  }, 1)
-                })
-              }
-            })
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><span>42</span> <div>vue.js</div></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('should merge serverPrefetch option', done => {
-    const mixin = {
-      data: {
-        message: ''
-      },
-      serverPrefetch() {
-        return new Promise<void>(resolve => {
-          setTimeout(() => {
-            this.message = 'vue.js'
-            resolve()
-          }, 1)
-        })
-      }
-    }
-    renderVmWithOptions(
-      {
-        mixins: [mixin],
-        template: `
-        <div>
-          <span>{{ count }}</span>
-          <div>{{ message }}</div>
-        </div>
-      `,
-        data: {
-          count: 0
-        },
-        serverPrefetch() {
-          return new Promise<void>(resolve => {
-            setTimeout(() => {
-              this.count = 42
-              resolve()
-            }, 1)
-          })
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><span>42</span> <div>vue.js</div></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it(
-    `should skip serverPrefetch option that doesn't return a promise`,
-    done => {
-      renderVmWithOptions(
-        {
-          template: `
-        <div>{{ count }}</div>
-      `,
-          data: {
-            count: 0
-          },
-          serverPrefetch() {
-            setTimeout(() => {
-              this.count = 42
-            }, 1)
-          }
-        },
-        result => {
-          expect(result).toContain('<div data-server-rendered="true">0</div>')
-          done()
-        }
-      )
-    }
-  )
-
-  _it('should call context.rendered', done => {
-    let a = 0
-    renderToString(
-      new Vue({
-        template: '<div>Hello</div>'
-      }),
-      {
-        rendered: () => {
-          a = 42
-        }
-      },
-      (err, res) => {
-        expect(err).toBeNull()
-        expect(res).toContain('<div data-server-rendered="true">Hello</div>')
-        expect(a).toBe(42)
-        done()
-      }
-    )
-  })
-
-  _it('invalid style value', done => {
-    renderVmWithOptions(
-      {
-        template: '<div :style="style"><p :style="style2"/></div>',
-        data: {
-          // all invalid, should not even have "style" attribute
-          style: {
-            opacity: {},
-            color: null
-          },
-          // mix of valid and invalid
-          style2: {
-            opacity: 0,
-            color: null
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><p style="opacity:0;"></p></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('numeric style value', done => {
-    renderVmWithOptions(
-      {
-        template: '<div :style="style"></div>',
-        data: {
-          style: {
-            opacity: 0, // valid, opacity is unit-less
-            top: 0, // valid, top requires unit but 0 is allowed
-            left: 10, // invalid, left requires a unit
-            marginTop: '10px' // valid
-          }
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true" style="opacity:0;top:0;margin-top:10px;"></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('handling max stack size limit', done => {
-    const vueInstance = new Vue({
-      template: `<div class="root">
-        <child v-for="(x, i) in items" :key="i"></child>
-      </div>`,
-      components: {
-        child: {
-          template: '<div class="child"><span class="child">hi</span></div>'
-        }
-      },
-      data: {
-        items: Array(1000).fill(0)
-      }
-    })
-
-    renderToString(vueInstance, err => done(err))
-  })
-
-  _it('undefined v-model with textarea', done => {
-    renderVmWithOptions(
-      {
-        render(h) {
-          return h('div', [
-            h('textarea', {
-              domProps: {
-                value: null
-              }
-            })
-          ])
-        }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><textarea></textarea></div>'
-        )
-        done()
-      }
-    )
-  })
-
-  _it('Options inheritAttrs in parent component', done => {
-    const childComponent = {
-      template: `<div>{{ someProp }}</div>`,
-      props: {
-        someProp: {}
-      }
-    }
-    const parentComponent = {
-      template: `<childComponent v-bind="$attrs" />`,
-      components: { childComponent },
-      inheritAttrs: false
-    }
-    renderVmWithOptions(
-      {
-        template: `
-        <div>
-          <parentComponent some-prop="some-val" />
-        </div>
-        `,
-        components: { parentComponent }
-      },
-      result => {
-        expect(result).toContain(
-          '<div data-server-rendered="true"><div>some-val</div></div>'
-        )
-        done()
-      }
-    )
-  })
-})
-
-function renderVmWithOptions(options, cb) {
-  renderToString(new Vue(options), (err, res) => {
-    expect(err).toBeNull()
-    cb(res)
-  })
-}
diff --git a/packages/server-renderer/test/ssr-template.spec.ts b/packages/server-renderer/test/ssr-template.spec.ts
deleted file mode 100644
index b173c439e99..00000000000
--- a/packages/server-renderer/test/ssr-template.spec.ts
+++ /dev/null
@@ -1,630 +0,0 @@
-// @vitest-environment node
-
-import Vue from 'vue'
-import {
-  compileWithWebpack,
-  createWebpackBundleRenderer
-} from './compile-with-webpack'
-import { createRenderer } from 'server/index'
-import VueSSRClientPlugin from 'server/webpack-plugin/client'
-import { RenderOptions } from 'server/create-renderer'
-
-const defaultTemplate = `<html><head></head><body><!--vue-ssr-outlet--></body></html>`
-const interpolateTemplate = `<html><head><title>{{ title }}</title></head><body><!--vue-ssr-outlet-->{{{ snippet }}}</body></html>`
-
-async function generateClientManifest(file: string) {
-  const fs = await compileWithWebpack(file, {
-    output: {
-      path: '/',
-      publicPath: '/',
-      filename: '[name].js'
-    },
-    optimization: {
-      runtimeChunk: {
-        name: 'manifest'
-      }
-    },
-    plugins: [new VueSSRClientPlugin()]
-  })
-  return JSON.parse(fs.readFileSync('/vue-ssr-client-manifest.json', 'utf-8'))
-}
-
-async function createRendererWithManifest(
-  file: string,
-  options?: RenderOptions
-) {
-  const clientManifest = await generateClientManifest(file)
-  return createWebpackBundleRenderer(
-    file,
-    Object.assign(
-      {
-        asBundle: true,
-        template: defaultTemplate,
-        clientManifest
-      },
-      options
-    )
-  )
-}
-
-describe('SSR: template option', () => {
-  it('renderToString', async () => {
-    const renderer = createRenderer({
-      template: defaultTemplate
-    })
-
-    const context = {
-      head: '<meta name="viewport" content="width=device-width">',
-      styles: '<style>h1 { color: red }</style>',
-      state: { a: 1 }
-    }
-
-    const res = await renderer.renderToString(
-      new Vue({
-        template: '<div>hi</div>'
-      }),
-      context
-    )
-
-    expect(res).toContain(
-      `<html><head>${context.head}${context.styles}</head><body>` +
-        `<div data-server-rendered="true">hi</div>` +
-        `<script>window.__INITIAL_STATE__={"a":1}</script>` +
-        `</body></html>`
-    )
-  })
-
-  it('renderToString with interpolation', async () => {
-    const renderer = createRenderer({
-      template: interpolateTemplate
-    })
-
-    const context = {
-      title: '<script>hacks</script>',
-      snippet: '<div>foo</div>',
-      head: '<meta name="viewport" content="width=device-width">',
-      styles: '<style>h1 { color: red }</style>',
-      state: { a: 1 }
-    }
-
-    const res = await renderer.renderToString(
-      new Vue({
-        template: '<div>hi</div>'
-      }),
-      context
-    )
-
-    expect(res).toContain(
-      `<html><head>` +
-        // double mustache should be escaped
-        `<title>&lt;script&gt;hacks&lt;/script&gt;</title>` +
-        `${context.head}${context.styles}</head><body>` +
-        `<div data-server-rendered="true">hi</div>` +
-        `<script>window.__INITIAL_STATE__={"a":1}</script>` +
-        // triple should be raw
-        `<div>foo</div>` +
-        `</body></html>`
-    )
-  })
-
-  it('renderToString with interpolation and context.rendered', async () => {
-    const renderer = createRenderer({
-      template: interpolateTemplate
-    })
-
-    const context = {
-      title: '<script>hacks</script>',
-      snippet: '<div>foo</div>',
-      head: '<meta name="viewport" content="width=device-width">',
-      styles: '<style>h1 { color: red }</style>',
-      state: { a: 0 },
-      rendered: context => {
-        context.state.a = 1
-      }
-    }
-
-    const res = await renderer.renderToString(
-      new Vue({
-        template: '<div>hi</div>'
-      }),
-      context
-    )
-    expect(res).toContain(
-      `<html><head>` +
-        // double mustache should be escaped
-        `<title>&lt;script&gt;hacks&lt;/script&gt;</title>` +
-        `${context.head}${context.styles}</head><body>` +
-        `<div data-server-rendered="true">hi</div>` +
-        `<script>window.__INITIAL_STATE__={"a":1}</script>` +
-        // triple should be raw
-        `<div>foo</div>` +
-        `</body></html>`
-    )
-  })
-
-  it('renderToString w/ template function', async () => {
-    const renderer = createRenderer({
-      template: (content, context) =>
-        `<html><head>${context.head}</head>${content}</html>`
-    })
-
-    const context = {
-      head: '<meta name="viewport" content="width=device-width">'
-    }
-
-    const res = await renderer.renderToString(
-      new Vue({
-        template: '<div>hi</div>'
-      }),
-      context
-    )
-
-    expect(res).toContain(
-      `<html><head>${context.head}</head><div data-server-rendered="true">hi</div></html>`
-    )
-  })
-
-  it('renderToString w/ template function returning Promise', async () => {
-    const renderer = createRenderer({
-      template: (content, context) =>
-        new Promise<string>(resolve => {
-          setTimeout(() => {
-            resolve(`<html><head>${context.head}</head>${content}</html>`)
-          }, 0)
-        })
-    })
-
-    const context = {
-      head: '<meta name="viewport" content="width=device-width">'
-    }
-
-    const res = await renderer.renderToString(
-      new Vue({
-        template: '<div>hi</div>'
-      }),
-      context
-    )
-
-    expect(res).toContain(
-      `<html><head>${context.head}</head><div data-server-rendered="true">hi</div></html>`
-    )
-  })
-
-  it('renderToString w/ template function returning Promise w/ rejection', async () => {
-    const renderer = createRenderer({
-      template: () =>
-        new Promise((resolve, reject) => {
-          setTimeout(() => {
-            reject(new Error(`foo`))
-          }, 0)
-        })
-    })
-
-    const context = {
-      head: '<meta name="viewport" content="width=device-width">'
-    }
-
-    try {
-      await renderer.renderToString(
-        new Vue({
-          template: '<div>hi</div>'
-        }),
-        context
-      )
-    } catch (err: any) {
-      expect(err.message).toBe(`foo`)
-    }
-  })
-
-  it('renderToStream', async () => {
-    const renderer = createRenderer({
-      template: defaultTemplate
-    })
-
-    const context = {
-      head: '<meta name="viewport" content="width=device-width">',
-      styles: '<style>h1 { color: red }</style>',
-      state: { a: 1 }
-    }
-
-    const res = await new Promise((resolve, reject) => {
-      const stream = renderer.renderToStream(
-        new Vue({
-          template: '<div>hi</div>'
-        }),
-        context
-      )
-
-      let res = ''
-      stream.on('data', chunk => {
-        res += chunk
-      })
-      stream.on('error', reject)
-      stream.on('end', () => {
-        resolve(res)
-      })
-    })
-
-    expect(res).toContain(
-      `<html><head>${context.head}${context.styles}</head><body>` +
-        `<div data-server-rendered="true">hi</div>` +
-        `<script>window.__INITIAL_STATE__={"a":1}</script>` +
-        `</body></html>`
-    )
-  })
-
-  it('renderToStream with interpolation', async () => {
-    const renderer = createRenderer({
-      template: interpolateTemplate
-    })
-
-    const context = {
-      title: '<script>hacks</script>',
-      snippet: '<div>foo</div>',
-      head: '<meta name="viewport" content="width=device-width">',
-      styles: '<style>h1 { color: red }</style>',
-      state: { a: 1 }
-    }
-
-    const res = await new Promise((resolve, reject) => {
-      const stream = renderer.renderToStream(
-        new Vue({
-          template: '<div>hi</div>'
-        }),
-        context
-      )
-
-      let res = ''
-      stream.on('data', chunk => {
-        res += chunk
-      })
-      stream.on('error', reject)
-      stream.on('end', () => {
-        resolve(res)
-      })
-    })
-
-    expect(res).toContain(
-      `<html><head>` +
-        // double mustache should be escaped
-        `<title>&lt;script&gt;hacks&lt;/script&gt;</title>` +
-        `${context.head}${context.styles}</head><body>` +
-        `<div data-server-rendered="true">hi</div>` +
-        `<script>window.__INITIAL_STATE__={"a":1}</script>` +
-        // triple should be raw
-        `<div>foo</div>` +
-        `</body></html>`
-    )
-  })
-
-  it('renderToStream with interpolation and context.rendered', async () => {
-    const renderer = createRenderer({
-      template: interpolateTemplate
-    })
-
-    const context = {
-      title: '<script>hacks</script>',
-      snippet: '<div>foo</div>',
-      head: '<meta name="viewport" content="width=device-width">',
-      styles: '<style>h1 { color: red }</style>',
-      state: { a: 0 },
-      rendered: context => {
-        context.state.a = 1
-      }
-    }
-
-    const res = await new Promise((resolve, reject) => {
-      const stream = renderer.renderToStream(
-        new Vue({
-          template: '<div>hi</div>'
-        }),
-        context
-      )
-
-      let res = ''
-      stream.on('data', chunk => {
-        res += chunk
-      })
-      stream.on('error', reject)
-      stream.on('end', () => {
-        resolve(res)
-      })
-    })
-
-    expect(res).toContain(
-      `<html><head>` +
-        // double mustache should be escaped
-        `<title>&lt;script&gt;hacks&lt;/script&gt;</title>` +
-        `${context.head}${context.styles}</head><body>` +
-        `<div data-server-rendered="true">hi</div>` +
-        `<script>window.__INITIAL_STATE__={"a":1}</script>` +
-        // triple should be raw
-        `<div>foo</div>` +
-        `</body></html>`
-    )
-  })
-
-  it('bundleRenderer + renderToString', async () => {
-    const renderer = await createWebpackBundleRenderer('app.js', {
-      asBundle: true,
-      template: defaultTemplate
-    })
-    const context: any = {
-      head: '<meta name="viewport" content="width=device-width">',
-      styles: '<style>h1 { color: red }</style>',
-      state: { a: 1 },
-      url: '/test'
-    }
-    const res = await renderer.renderToString(context)
-    expect(res).toContain(
-      `<html><head>${context.head}${context.styles}</head><body>` +
-        `<div data-server-rendered="true">/test</div>` +
-        `<script>window.__INITIAL_STATE__={"a":1}</script>` +
-        `</body></html>`
-    )
-    expect(context.msg).toBe('hello')
-  })
-
-  it('bundleRenderer + renderToStream', async () => {
-    const renderer = await createWebpackBundleRenderer('app.js', {
-      asBundle: true,
-      template: defaultTemplate
-    })
-    const context: any = {
-      head: '<meta name="viewport" content="width=device-width">',
-      styles: '<style>h1 { color: red }</style>',
-      state: { a: 1 },
-      url: '/test'
-    }
-
-    const res = await new Promise(resolve => {
-      const stream = renderer.renderToStream(context)
-      let res = ''
-      stream.on('data', chunk => {
-        res += chunk.toString()
-      })
-      stream.on('end', () => {
-        resolve(res)
-      })
-    })
-
-    expect(res).toContain(
-      `<html><head>${context.head}${context.styles}</head><body>` +
-        `<div data-server-rendered="true">/test</div>` +
-        `<script>window.__INITIAL_STATE__={"a":1}</script>` +
-        `</body></html>`
-    )
-    expect(context.msg).toBe('hello')
-  })
-
-  const expectedHTMLWithManifest = (options: any = {}) =>
-    `<html><head>` +
-    // used chunks should have preload
-    `<link rel="preload" href="/manifest.js" as="script">` +
-    `<link rel="preload" href="/main.js" as="script">` +
-    `<link rel="preload" href="/0.js" as="script">` +
-    `<link rel="preload" href="/test.css" as="style">` +
-    // images and fonts are only preloaded when explicitly asked for
-    (options.preloadOtherAssets
-      ? `<link rel="preload" href="/test.png" as="image">`
-      : ``) +
-    (options.preloadOtherAssets
-      ? `<link rel="preload" href="/test.woff2" as="font" type="font/woff2" crossorigin>`
-      : ``) +
-    // unused chunks should have prefetch
-    (options.noPrefetch ? `` : `<link rel="prefetch" href="/1.js">`) +
-    // css assets should be loaded
-    `<link rel="stylesheet" href="/test.css">` +
-    `</head><body>` +
-    `<div data-server-rendered="true"><div>async test.woff2 test.png</div></div>` +
-    // state should be inlined before scripts
-    `<script>window.${
-      options.stateKey || '__INITIAL_STATE__'
-    }={"a":1}</script>` +
-    // manifest chunk should be first
-    `<script src="/manifest.js" defer></script>` +
-    // async chunks should be before main chunk
-    `<script src="/0.js" defer></script>` +
-    `<script src="/main.js" defer></script>` +
-    `</body></html>`
-
-  createClientManifestAssertions(true)
-  createClientManifestAssertions(false)
-
-  function createClientManifestAssertions(runInNewContext) {
-    it('bundleRenderer + renderToString + clientManifest ()', async () => {
-      const renderer = await createRendererWithManifest('split.js', {
-        runInNewContext
-      })
-      const res = await renderer.renderToString({ state: { a: 1 } })
-      expect(res).toContain(expectedHTMLWithManifest())
-    })
-
-    it('bundleRenderer + renderToStream + clientManifest + shouldPreload', async () => {
-      const renderer = await createRendererWithManifest('split.js', {
-        runInNewContext,
-        shouldPreload: (file, type) => {
-          if (
-            type === 'image' ||
-            type === 'script' ||
-            type === 'font' ||
-            type === 'style'
-          ) {
-            return true
-          }
-        }
-      })
-      const res = await new Promise(resolve => {
-        const stream = renderer.renderToStream({ state: { a: 1 } })
-        let res = ''
-        stream.on('data', chunk => {
-          res += chunk.toString()
-        })
-        stream.on('end', () => {
-          resolve(res)
-        })
-      })
-
-      expect(res).toContain(
-        expectedHTMLWithManifest({
-          preloadOtherAssets: true
-        })
-      )
-    })
-
-    it('bundleRenderer + renderToStream + clientManifest + shouldPrefetch', async () => {
-      const renderer = await createRendererWithManifest('split.js', {
-        runInNewContext,
-        shouldPrefetch: (file, type) => {
-          if (type === 'script') {
-            return false
-          }
-        }
-      })
-
-      const res = await new Promise(resolve => {
-        const stream = renderer.renderToStream({ state: { a: 1 } })
-        let res = ''
-        stream.on('data', chunk => {
-          res += chunk.toString()
-        })
-        stream.on('end', () => {
-          resolve(res)
-        })
-      })
-
-      expect(res).toContain(
-        expectedHTMLWithManifest({
-          noPrefetch: true
-        })
-      )
-    })
-
-    it('bundleRenderer + renderToString + clientManifest + inject: false', async () => {
-      const renderer = await createRendererWithManifest('split.js', {
-        runInNewContext,
-        template:
-          `<html>` +
-          `<head>{{{ renderResourceHints() }}}{{{ renderStyles() }}}</head>` +
-          `<body><!--vue-ssr-outlet-->{{{ renderState({ windowKey: '__FOO__', contextKey: 'foo' }) }}}{{{ renderScripts() }}}</body>` +
-          `</html>`,
-        inject: false
-      })
-      const context = { foo: { a: 1 } }
-      const res = await renderer.renderToString(context)
-      expect(res).toContain(
-        expectedHTMLWithManifest({
-          stateKey: '__FOO__'
-        })
-      )
-    })
-
-    it('bundleRenderer + renderToString + clientManifest + no template', async () => {
-      const renderer = await createRendererWithManifest('split.js', {
-        runInNewContext,
-        template: null as any
-      })
-      const context: any = { foo: { a: 1 } }
-      const res = await renderer.renderToString(context)
-
-      const customOutput = `<html><head>${
-        context.renderResourceHints() + context.renderStyles()
-      }</head><body>${
-        res +
-        context.renderState({
-          windowKey: '__FOO__',
-          contextKey: 'foo'
-        }) +
-        context.renderScripts()
-      }</body></html>`
-
-      expect(customOutput).toContain(
-        expectedHTMLWithManifest({
-          stateKey: '__FOO__'
-        })
-      )
-    })
-
-    it('whitespace insensitive interpolation', async () => {
-      const interpolateTemplate = `<html><head><title>{{title}}</title></head><body><!--vue-ssr-outlet-->{{{snippet}}}</body></html>`
-      const renderer = createRenderer({
-        template: interpolateTemplate
-      })
-
-      const context = {
-        title: '<script>hacks</script>',
-        snippet: '<div>foo</div>',
-        head: '<meta name="viewport" content="width=device-width">',
-        styles: '<style>h1 { color: red }</style>',
-        state: { a: 1 }
-      }
-
-      const res = await renderer.renderToString(
-        new Vue({
-          template: '<div>hi</div>'
-        }),
-        context
-      )
-      expect(res).toContain(
-        `<html><head>` +
-          // double mustache should be escaped
-          `<title>&lt;script&gt;hacks&lt;/script&gt;</title>` +
-          `${context.head}${context.styles}</head><body>` +
-          `<div data-server-rendered="true">hi</div>` +
-          `<script>window.__INITIAL_STATE__={"a":1}</script>` +
-          // triple should be raw
-          `<div>foo</div>` +
-          `</body></html>`
-      )
-    })
-
-    it('renderToString + nonce', async () => {
-      const interpolateTemplate = `<html><head><title>hello</title></head><body><!--vue-ssr-outlet--></body></html>`
-      const renderer = createRenderer({
-        template: interpolateTemplate
-      })
-
-      const context = {
-        state: { a: 1 },
-        nonce: '4AEemGb0xJptoIGFP3Nd'
-      }
-
-      const res = await renderer.renderToString(
-        new Vue({
-          template: '<div>hi</div>'
-        }),
-        context
-      )
-      expect(res).toContain(
-        `<html><head>` +
-          `<title>hello</title>` +
-          `</head><body>` +
-          `<div data-server-rendered="true">hi</div>` +
-          `<script nonce="4AEemGb0xJptoIGFP3Nd">window.__INITIAL_STATE__={"a":1}</script>` +
-          `</body></html>`
-      )
-    })
-
-    it('renderToString + custom serializer', async () => {
-      const expected = `{"foo":123}`
-      const renderer = createRenderer({
-        template: defaultTemplate,
-        serializer: () => expected
-      })
-
-      const context = {
-        state: { a: 1 }
-      }
-
-      const res = await renderer.renderToString(
-        new Vue({
-          template: '<div>hi</div>'
-        }),
-        context
-      )
-      expect(res).toContain(
-        `<script>window.__INITIAL_STATE__=${expected}</script>`
-      )
-    })
-  }
-})
diff --git a/packages/server-renderer/test/tsconfig.json b/packages/server-renderer/test/tsconfig.json
deleted file mode 100644
index 96cc3dcb9a0..00000000000
--- a/packages/server-renderer/test/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "extends": "../../../tsconfig.json",
-  "compilerOptions": {
-    "types": ["node", "vitest/globals"]
-  },
-  "include": [".", "../../../test/test-env.d.ts"]
-}
diff --git a/packages/server-renderer/test/utils.ts b/packages/server-renderer/test/utils.ts
deleted file mode 100644
index 0ba53383478..00000000000
--- a/packages/server-renderer/test/utils.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Async callback style it for compatibility with old tests.
- */
-export function _it(
-  desc: string,
-  runner: (done: (err?: Error) => void) => void | Promise<any>
-) {
-  it(desc, async () => {
-    if (runner.length === 0) {
-      return runner(() => {})
-    }
-    await new Promise<void>((resolve, reject) => {
-      const res = runner(err => {
-        if (err) reject(err)
-        else resolve()
-      })
-      if (res instanceof Promise) {
-        resolve(res)
-      }
-    })
-  })
-}
diff --git a/packages/server-renderer/types/index.d.ts b/packages/server-renderer/types/index.d.ts
deleted file mode 100644
index 6932cd1430f..00000000000
--- a/packages/server-renderer/types/index.d.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import Vue, { VNode, VNodeDirective } from 'vue'
-import { Readable } from 'stream'
-
-export declare function createRenderer(options?: RendererOptions): Renderer
-
-export declare function createBundleRenderer(
-  bundle: string | object,
-  options?: BundleRendererOptions
-): BundleRenderer
-
-type RenderCallback = (err: Error | null, html: string) => void
-
-interface Renderer {
-  renderToString(vm: Vue, callback: RenderCallback): void
-  renderToString(vm: Vue, context: object, callback: RenderCallback): void
-  renderToString(vm: Vue): Promise<string>
-  renderToString(vm: Vue, context: object): Promise<string>
-
-  renderToStream(vm: Vue, context?: object): Readable
-}
-
-interface BundleRenderer {
-  renderToString(callback: RenderCallback): void
-  renderToString(context: object, callback: RenderCallback): void
-  renderToString(): Promise<string>
-  renderToString(context: object): Promise<string>
-
-  renderToStream(context?: object): Readable
-}
-
-interface RendererOptions {
-  template?: string
-  inject?: boolean
-  shouldPreload?: (file: string, type: string) => boolean
-  shouldPrefetch?: (file: string, type: string) => boolean
-  cache?: RenderCache
-  directives?: {
-    [key: string]: (vnode: VNode, dir: VNodeDirective) => void
-  }
-}
-
-interface BundleRendererOptions extends RendererOptions {
-  clientManifest?: object
-  serializer?: (state: object) => string
-  runInNewContext?: boolean | 'once'
-  basedir?: string
-}
-
-interface RenderCache {
-  get: (key: string, cb?: (res: string) => void) => string | void
-  set: (key: string, val: string) => void
-  has?: (key: string, cb?: (hit: boolean) => void) => boolean | void
-}
diff --git a/packages/server-renderer/types/plugin.d.ts b/packages/server-renderer/types/plugin.d.ts
deleted file mode 100644
index 423a58b4d81..00000000000
--- a/packages/server-renderer/types/plugin.d.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { DefinePlugin } from 'webpack'
-
-interface WebpackPluginOptions {
-  filename?: string
-}
-
-export interface WebpackPlugin {
-  // NOTE NOT SURE ABOUT THIS
-  // TODO DOUBLE CHECK HERE
-  new (options?: WebpackPluginOptions): DefinePlugin
-}
diff --git a/packages/server-renderer/types/test.ts b/packages/server-renderer/types/test.ts
deleted file mode 100644
index 7a353066eda..00000000000
--- a/packages/server-renderer/types/test.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-import Vue, { VNode, VNodeDirective } from '../../../types/index'
-import VueSSRClientPlugin from '../client-plugin'
-import VueSSRServerPlugin from '../server-plugin'
-import webpack from 'webpack'
-import { readFileSync } from 'fs'
-import { createRenderer, createBundleRenderer } from '.'
-
-function createApp(context: any) {
-  return new Vue({
-    data: {
-      url: context.url
-    },
-    template: `<div>The visited URL is: {{ url }}</div>`
-  })
-}
-
-// Renderer test
-const app = createApp({ url: 'http://localhost:8000/' })
-
-const renderer = createRenderer({
-  template: readFileSync('./index.template.html', 'utf-8')
-})
-
-const context = {
-  title: 'Hello',
-  meta: `
-    <meta name="description" content="Vue.js SSR Example">
-  `
-}
-
-renderer.renderToString(app, (err, html) => {
-  if (err) throw err
-  const res: string = html
-})
-
-renderer.renderToString(app, context, (err, html) => {
-  if (err) throw err
-  const res: string = html
-})
-
-renderer
-  .renderToString(app)
-  .then(html => {
-    const res: string = html
-  })
-  .catch(err => {
-    throw err
-  })
-
-renderer
-  .renderToString(app, context)
-  .then(html => {
-    const res: string = html
-  })
-  .catch(err => {
-    throw err
-  })
-
-renderer.renderToStream(app, context).on('data', chunk => {
-  const html = chunk.toString()
-})
-
-// Bundle renderer test
-declare const cacheClient: { [key: string]: string }
-
-const bundleRenderer = createBundleRenderer(
-  '/path/to/vue-ssr-server-bundle.json',
-  {
-    inject: false,
-    runInNewContext: 'once',
-    basedir: '/path/to/base',
-
-    shouldPreload: (file, type) => {
-      if (type === 'script' || type === 'style') {
-        return true
-      }
-      if (type === 'font') {
-        return /\.woff2$/.test(file)
-      }
-      if (type === 'image') {
-        return file === 'hero.jpg'
-      }
-      return false
-    },
-
-    cache: {
-      get: key => {
-        return cacheClient[key]
-      },
-      set: (key, val) => {
-        cacheClient[key] = val
-      },
-      has: key => {
-        return !!cacheClient[key]
-      }
-    },
-
-    directives: {
-      example(vnode: VNode, directiveMeta: VNodeDirective) {
-        // transform vnode based on directive binding metadata
-      }
-    }
-  }
-)
-
-bundleRenderer.renderToString(context, (err, html) => {
-  if (err) throw err
-  const res: string = html
-})
-
-bundleRenderer.renderToString().then(html => {
-  const res: string = html
-})
-
-bundleRenderer.renderToString(context).then(html => {
-  const res: string = html
-})
-
-bundleRenderer.renderToStream(context).on('data', chunk => {
-  const html = chunk.toString()
-})
-
-// webpack plugins
-webpack({
-  plugins: [
-    new VueSSRClientPlugin({
-      filename: 'client-manifest.json'
-    }),
-    new VueSSRServerPlugin({
-      filename: 'server-bundle.json'
-    })
-  ]
-})
diff --git a/packages/server-renderer/types/tsconfig.json b/packages/server-renderer/types/tsconfig.json
deleted file mode 100644
index 465615893cd..00000000000
--- a/packages/server-renderer/types/tsconfig.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "compilerOptions": {
-    "target": "esnext",
-    "module": "esnext",
-    "moduleResolution": "node",
-    "strict": true,
-    "noEmit": true,
-    "allowSyntheticDefaultImports": true,
-    "paths": {
-      "vue": ["../../../types/index.d.ts"]
-    }
-  },
-  "include": ["**/*.ts", "../../../types"]
-}
diff --git a/packages/template-compiler/README.md b/packages/template-compiler/README.md
deleted file mode 100644
index ac14f72e9fc..00000000000
--- a/packages/template-compiler/README.md
+++ /dev/null
@@ -1,162 +0,0 @@
-# vue-template-compiler
-
-> This package is auto-generated. For pull requests please see [src/platforms/web/entry-compiler.js](https://github.com/vuejs/vue/tree/dev/src/platforms/web/entry-compiler.js).
-
-This package can be used to pre-compile Vue 2.0 templates into render functions to avoid runtime-compilation overhead and CSP restrictions. In most cases you should be using it with [`vue-loader`](https://github.com/vuejs/vue-loader), you will only need it separately if you are writing build tools with very specific needs.
-
-## Installation
-
-``` bash
-npm install vue-template-compiler
-```
-
-``` js
-const compiler = require('vue-template-compiler')
-```
-
-## API
-
-### compiler.compile(template, [options])
-
-Compiles a template string and returns compiled JavaScript code. The returned result is an object of the following format:
-
-``` js
-{
-  ast: ?ASTElement, // parsed template elements to AST
-  render: string, // main render function code
-  staticRenderFns: Array<string>, // render code for static sub trees, if any
-  errors: Array<string> // template syntax errors, if any
-}
-```
-
-Note the returned function code uses `with` and thus cannot be used in strict mode code.
-
-#### Options
-
-- `outputSourceRange` *new in 2.6*
-  - Type: `boolean`
-  - Default: `false`
-
-  Set this to true will cause the `errors` returned in the compiled result become objects in the form of `{ msg, start, end }`. The `start` and `end` properties are numbers that mark the code range of the error source in the template. This can be passed on to the `compiler.generateCodeFrame` API to generate a code frame for the error.
-
-- `whitespace`
-  - Type: `string`
-  - Valid values: `'preserve' | 'condense'`
-  - Default: `'preserve'`
-
-  The default value `'preserve'` handles whitespaces as follows:
-
-  - A whitespace-only text node between element tags is condensed into a single space.
-  - All other whitespaces are preserved as-is.
-
-  If set to `'condense'`:
-
-  - A whitespace-only text node between element tags is removed if it contains new lines. Otherwise, it is condensed into a single space.
-  - Consecutive whitespaces inside a non-whitespace-only text node are condensed into a single space.
-
-  Using condense mode will result in smaller compiled code size and slightly improved performance. However, it will produce minor visual layout differences compared to plain HTML in certain cases.
-
-  **This option does not affect the `<pre>` tag.**
-
-  Example:
-
-  ``` html
-  <!-- source -->
-  <div>
-    <span>
-      foo
-    </span>   <span>bar</span>
-  </div>
-
-  <!-- whitespace: 'preserve' -->
-  <div> <span>
-    foo
-    </span> <span>bar</span> </div>
-
-  <!-- whitespace: 'condense' -->
-  <div><span> foo </span> <span>bar</span></div>
-  ```
-
-- `modules`
-
-  It's possible to hook into the compilation process to support custom template features. **However, beware that by injecting custom compile-time modules, your templates will not work with other build tools built on standard built-in modules, e.g `vue-loader` and `vueify`.**
-
-  An array of compiler modules. For details on compiler modules, refer to the `ModuleOptions` type in [flow declarations](https://github.com/vuejs/vue/blob/dev/flow/compiler.js#L47-L59) and the [built-in modules](https://github.com/vuejs/vue/tree/dev/src/platforms/web/compiler/modules).
-
-- `directives`
-
-  An object where the key is the directive name and the value is a function that transforms an template AST node. For example:
-
-  ``` js
-  compiler.compile('<div v-test></div>', {
-    directives: {
-      test (node, directiveMeta) {
-        // transform node based on directiveMeta
-      }
-    }
-  })
-  ```
-
-  By default, a compile-time directive will extract the directive and the directive will not be present at runtime. If you want the directive to also be handled by a runtime definition, return `true` in the transform function.
-
-  Refer to the implementation of some [built-in compile-time directives](https://github.com/vuejs/vue/tree/dev/src/platforms/web/compiler/directives).
-
-- `preserveWhitespace` **Deprecated since 2.6**
-  - Type: `boolean`
-  - Default: `true`
-
-  By default, the compiled render function preserves all whitespace characters between HTML tags. If set to `false`, whitespace between tags will be ignored. This can result in slightly better performance but may affect layout for inline elements.
-
----
-
-### compiler.compileToFunctions(template)
-
-Similar to `compiler.compile`, but directly returns instantiated functions:
-
-``` js
-{
-  render: Function,
-  staticRenderFns: Array<Function>
-}
-```
-
-This is only useful at runtime with pre-configured builds, so it doesn't accept any compile-time options. In addition, this method uses `new Function()` so it is not CSP-compliant.
-
----
-
-### compiler.ssrCompile(template, [options])
-
-> 2.4.0+
-
-Same as `compiler.compile` but generates SSR-specific render function code by optimizing parts of the template into string concatenation in order to improve SSR performance.
-
-This is used by default in `vue-loader@>=12` and can be disabled using the [`optimizeSSR`](https://vue-loader.vuejs.org/options.html#optimizessr) option.
-
----
-
-### compiler.ssrCompileToFunctions(template)
-
-> 2.4.0+
-
-Same as `compiler.compileToFunction` but generates SSR-specific render function code by optimizing parts of the template into string concatenation in order to improve SSR performance.
-
----
-
-### compiler.parseComponent(file, [options])
-
-Parse a SFC (single-file component, or `*.vue` file) into a descriptor (refer to the `SFCDescriptor` type in [flow declarations](https://github.com/vuejs/vue/blob/dev/flow/compiler.js)). This is used in SFC build tools like `vue-loader` and `vueify`.
-
----
-
-### compiler.generateCodeFrame(template, start, end)
-
-Generate a code frame that highlights the part in `template` defined by `start` and `end`. Useful for error reporting in higher-level tooling.
-
-#### Options
-
-#### `pad`
-
-`pad` is useful when you are piping the extracted content into other pre-processors, as you will get correct line numbers or character indices if there are any syntax errors.
-
-- with `{ pad: "line" }`, the extracted content for each block will be prefixed with one newline for each line in the leading content from the original file to ensure that the line numbers align with the original file.
-- with `{ pad: "space" }`, the extracted content for each block will be prefixed with one space for each character in the leading content from the original file to ensure that the character count remains the same as the original file.
diff --git a/packages/template-compiler/index.js b/packages/template-compiler/index.js
deleted file mode 100644
index 378a17dfa24..00000000000
--- a/packages/template-compiler/index.js
+++ /dev/null
@@ -1,32 +0,0 @@
-try {
-  var vueVersion = require('vue').version
-} catch (e) {}
-
-var packageName = require('./package.json').name
-var packageVersion = require('./package.json').version
-if (vueVersion && vueVersion !== packageVersion) {
-  var vuePath = require.resolve('vue')
-  var packagePath = require.resolve('./package.json')
-  throw new Error(
-    '\n\nVue packages version mismatch:\n\n' +
-      '- vue@' +
-      vueVersion +
-      ' (' +
-      vuePath +
-      ')\n' +
-      '- ' +
-      packageName +
-      '@' +
-      packageVersion +
-      ' (' +
-      packagePath +
-      ')\n\n' +
-      'This may cause things to work incorrectly. Make sure to use the same version for both.\n' +
-      'If you are using vue-loader@>=10.0, simply update vue-template-compiler.\n' +
-      'If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump ' +
-      packageName +
-      ' to the latest.\n'
-  )
-}
-
-module.exports = require('./build')
diff --git a/packages/template-compiler/package.json b/packages/template-compiler/package.json
deleted file mode 100644
index 78d6a74e42e..00000000000
--- a/packages/template-compiler/package.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "name": "vue-template-compiler",
-  "version": "2.7.16",
-  "description": "template compiler for Vue 2.0",
-  "main": "index.js",
-  "unpkg": "browser.js",
-  "jsdelivr": "browser.js",
-  "browser": "browser.js",
-  "types": "types/index.d.ts",
-  "files": [
-    "types/*.d.ts",
-    "*.js"
-  ],
-  "repository": {
-    "type": "git",
-    "url": "git+https://github.com/vuejs/vue.git"
-  },
-  "keywords": [
-    "vue",
-    "compiler"
-  ],
-  "author": "Evan You",
-  "license": "MIT",
-  "bugs": {
-    "url": "https://github.com/vuejs/vue/issues"
-  },
-  "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-template-compiler#readme",
-  "dependencies": {
-    "de-indent": "^1.0.2",
-    "he": "^1.2.0"
-  },
-  "devDependencies": {
-    "vue": "file:../.."
-  }
-}
diff --git a/packages/template-compiler/types/index.d.ts b/packages/template-compiler/types/index.d.ts
deleted file mode 100644
index 105a52c9e94..00000000000
--- a/packages/template-compiler/types/index.d.ts
+++ /dev/null
@@ -1,247 +0,0 @@
-import { VNode } from 'vue'
-
-/*
- * Template compilation options / results
- */
-interface CompilerOptions {
-  modules?: ModuleOptions[]
-  directives?: Record<string, DirectiveFunction>
-  preserveWhitespace?: boolean
-  whitespace?: 'preserve' | 'condense'
-  outputSourceRange?: any
-}
-
-interface CompilerOptionsWithSourceRange extends CompilerOptions {
-  outputSourceRange: true
-}
-
-interface ErrorWithRange {
-  msg: string
-  start: number
-  end: number
-}
-
-interface CompiledResult<ErrorType> {
-  ast: ASTElement | undefined
-  render: string
-  staticRenderFns: string[]
-  errors: ErrorType[]
-  tips: ErrorType[]
-}
-
-interface CompiledResultFunctions {
-  render: () => VNode
-  staticRenderFns: (() => VNode)[]
-}
-
-interface ModuleOptions {
-  preTransformNode: (el: ASTElement) => ASTElement | undefined
-  transformNode: (el: ASTElement) => ASTElement | undefined
-  postTransformNode: (el: ASTElement) => void
-  genData: (el: ASTElement) => string
-  transformCode?: (el: ASTElement, code: string) => string
-  staticKeys?: string[]
-}
-
-type DirectiveFunction = (node: ASTElement, directiveMeta: ASTDirective) => void
-
-/*
- * AST Types
- */
-
-/**
- * - 0: FALSE - whole sub tree un-optimizable
- * - 1: FULL - whole sub tree optimizable
- * - 2: SELF - self optimizable but has some un-optimizable children
- * - 3: CHILDREN - self un-optimizable but have fully optimizable children
- * - 4: PARTIAL - self un-optimizable with some un-optimizable children
- */
-export type SSROptimizability = 0 | 1 | 2 | 3 | 4
-
-export interface ASTModifiers {
-  [key: string]: boolean
-}
-
-export interface ASTIfCondition {
-  exp: string | undefined
-  block: ASTElement
-}
-
-export interface ASTElementHandler {
-  value: string
-  params?: any[]
-  modifiers: ASTModifiers | undefined
-}
-
-export interface ASTElementHandlers {
-  [key: string]: ASTElementHandler | ASTElementHandler[]
-}
-
-export interface ASTDirective {
-  name: string
-  rawName: string
-  value: string
-  arg: string | undefined
-  modifiers: ASTModifiers | undefined
-}
-
-export type ASTNode = ASTElement | ASTText | ASTExpression
-
-export interface ASTElement {
-  type: 1
-  tag: string
-  attrsList: { name: string; value: any }[]
-  attrsMap: Record<string, any>
-  parent: ASTElement | undefined
-  children: ASTNode[]
-
-  processed?: true
-
-  static?: boolean
-  staticRoot?: boolean
-  staticInFor?: boolean
-  staticProcessed?: boolean
-  hasBindings?: boolean
-
-  text?: string
-  attrs?: { name: string; value: any }[]
-  props?: { name: string; value: string }[]
-  plain?: boolean
-  pre?: true
-  ns?: string
-
-  component?: string
-  inlineTemplate?: true
-  transitionMode?: string | null
-  slotName?: string
-  slotTarget?: string
-  slotScope?: string
-  scopedSlots?: Record<string, ASTElement>
-
-  ref?: string
-  refInFor?: boolean
-
-  if?: string
-  ifProcessed?: boolean
-  elseif?: string
-  else?: true
-  ifConditions?: ASTIfCondition[]
-
-  for?: string
-  forProcessed?: boolean
-  key?: string
-  alias?: string
-  iterator1?: string
-  iterator2?: string
-
-  staticClass?: string
-  classBinding?: string
-  staticStyle?: string
-  styleBinding?: string
-  events?: ASTElementHandlers
-  nativeEvents?: ASTElementHandlers
-
-  transition?: string | true
-  transitionOnAppear?: boolean
-
-  model?: {
-    value: string
-    callback: string
-    expression: string
-  }
-
-  directives?: ASTDirective[]
-
-  forbidden?: true
-  once?: true
-  onceProcessed?: boolean
-  wrapData?: (code: string) => string
-  wrapListeners?: (code: string) => string
-
-  // 2.4 ssr optimization
-  ssrOptimizability?: SSROptimizability
-}
-
-export interface ASTExpression {
-  type: 2
-  expression: string
-  text: string
-  tokens: (string | Record<string, any>)[]
-  static?: boolean
-  // 2.4 ssr optimization
-  ssrOptimizability?: SSROptimizability
-}
-
-export interface ASTText {
-  type: 3
-  text: string
-  static?: boolean
-  isComment?: boolean
-  // 2.4 ssr optimization
-  ssrOptimizability?: SSROptimizability
-}
-
-/*
- * SFC parser related types
- */
-interface SFCParserOptions {
-  pad?: true | 'line' | 'space'
-  deindent?: boolean
-}
-
-export interface SFCBlock {
-  type: string
-  content: string
-  attrs: Record<string, string>
-  start?: number
-  end?: number
-  lang?: string
-  src?: string
-  scoped?: boolean
-  module?: string | boolean
-}
-
-export interface SFCDescriptor {
-  template: SFCBlock | undefined
-  script: SFCBlock | undefined
-  styles: SFCBlock[]
-  customBlocks: SFCBlock[]
-}
-
-/*
- * Exposed functions
- */
-export function compile(
-  template: string,
-  options: CompilerOptionsWithSourceRange
-): CompiledResult<ErrorWithRange>
-
-export function compile(
-  template: string,
-  options?: CompilerOptions
-): CompiledResult<string>
-
-export function compileToFunctions(template: string): CompiledResultFunctions
-
-export function ssrCompile(
-  template: string,
-  options: CompilerOptionsWithSourceRange
-): CompiledResult<ErrorWithRange>
-
-export function ssrCompile(
-  template: string,
-  options?: CompilerOptions
-): CompiledResult<string>
-
-export function ssrCompileToFunctions(template: string): CompiledResultFunctions
-
-export function parseComponent(
-  file: string,
-  options?: SFCParserOptions
-): SFCDescriptor
-
-export function generateCodeFrame(
-  template: string,
-  start: number,
-  end: number
-): string
diff --git a/packages/template-compiler/types/test.ts b/packages/template-compiler/types/test.ts
deleted file mode 100644
index 09202c90c5e..00000000000
--- a/packages/template-compiler/types/test.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-import Vue, { VNode } from 'vue'
-import {
-  compile,
-  compileToFunctions,
-  ssrCompile,
-  ssrCompileToFunctions,
-  parseComponent,
-  generateCodeFrame
-} from '.'
-
-// check compile options
-const compiled = compile('<div>hi</div>', {
-  outputSourceRange: true,
-  preserveWhitespace: false,
-  whitespace: 'condense',
-  modules: [
-    {
-      preTransformNode: el => el,
-      transformNode: el => el,
-      postTransformNode: el => {
-        el.tag = 'p'
-      },
-      genData: el => el.tag,
-      transformCode: (el, code) => code,
-      staticKeys: ['test']
-    }
-  ],
-  directives: {
-    test: (node, directiveMeta) => {
-      node.tag
-      directiveMeta.value
-    }
-  }
-})
-
-// can be passed to function constructor
-new Function(compiled.render)
-compiled.staticRenderFns.map(fn => new Function(fn))
-
-// with outputSourceRange: true
-// errors should be objects with range
-compiled.errors.forEach(e => {
-  console.log(e.msg)
-})
-
-// without option or without outputSourceRange: true, should be strings
-const { errors } = compile(`foo`)
-errors.forEach(e => {
-  console.log(e.length)
-})
-
-const { errors: errors2 } = compile(`foo`, {})
-errors2.forEach(e => {
-  console.log(e.length)
-})
-
-const { errors: errors3 } = compile(`foo`, {
-  outputSourceRange: false
-})
-errors3.forEach(e => {
-  console.log(e.length)
-})
-
-const compiledFns = compileToFunctions('<div>hi</div>')
-
-// can be passed to component render / staticRenderFns options
-const vm = new Vue({
-  data() {
-    return {
-      test: 'Test'
-    }
-  },
-  render: compiledFns.render,
-  staticRenderFns: compiledFns.staticRenderFns
-})
-
-// can be called with component instance
-const vnode: VNode = compiledFns.render.call(vm)
-
-// check SFC parser
-const desc = parseComponent('<template></template>', {
-  pad: 'space',
-  deindent: false
-})
-
-const templateContent: string = desc.template!.content
-const scriptContent: string = desc.script!.content
-const styleContent: string = desc.styles.map(s => s.content).join('\n')
-
-const codeframe: string = generateCodeFrame(`foobar`, 0, 4)
diff --git a/packages/template-compiler/types/tsconfig.json b/packages/template-compiler/types/tsconfig.json
deleted file mode 100644
index 5e89be1ef9d..00000000000
--- a/packages/template-compiler/types/tsconfig.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "compilerOptions": {
-    "target": "esnext",
-    "module": "esnext",
-    "moduleResolution": "node",
-    "strict": true,
-    "noEmit": true,
-    "paths": {
-      "vue": ["../../../types/index.d.ts"]
-    }
-  },
-  "include": ["**/*.ts", "../../../types"]
-}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
deleted file mode 100644
index 5a9fd448a56..00000000000
--- a/pnpm-lock.yaml
+++ /dev/null
@@ -1,7017 +0,0 @@
-lockfileVersion: '6.0'
-
-settings:
-  autoInstallPeers: true
-  excludeLinksFromLockfile: false
-
-importers:
-
-  .:
-    dependencies:
-      '@vue/compiler-sfc':
-        specifier: workspace:*
-        version: link:packages/compiler-sfc
-      csstype:
-        specifier: ^3.1.0
-        version: 3.1.2
-    devDependencies:
-      '@babel/parser':
-        specifier: ^7.23.5
-        version: 7.23.5
-      '@microsoft/api-extractor':
-        specifier: ^7.25.0
-        version: 7.38.4(@types/node@20.10.3)
-      '@rollup/plugin-alias':
-        specifier: ^3.1.9
-        version: 3.1.9(rollup@2.79.1)
-      '@rollup/plugin-commonjs':
-        specifier: ^22.0.0
-        version: 22.0.2(rollup@2.79.1)
-      '@rollup/plugin-node-resolve':
-        specifier: ^13.3.0
-        version: 13.3.0(rollup@2.79.1)
-      '@rollup/plugin-replace':
-        specifier: ^4.0.0
-        version: 4.0.0(rollup@2.79.1)
-      '@types/he':
-        specifier: ^1.1.2
-        version: 1.2.3
-      '@types/node':
-        specifier: ^20.10.3
-        version: 20.10.3
-      chalk:
-        specifier: ^4.1.2
-        version: 4.1.2
-      conventional-changelog-cli:
-        specifier: ^2.2.2
-        version: 2.2.2
-      cross-spawn:
-        specifier: ^7.0.3
-        version: 7.0.3
-      enquirer:
-        specifier: ^2.3.6
-        version: 2.4.1
-      esbuild:
-        specifier: ^0.19.8
-        version: 0.19.8
-      execa:
-        specifier: ^4.1.0
-        version: 4.1.0
-      he:
-        specifier: ^1.2.0
-        version: 1.2.0
-      jasmine-core:
-        specifier: ^4.2.0
-        version: 4.6.0
-      jsdom:
-        specifier: ^19.0.0
-        version: 19.0.0
-      karma:
-        specifier: ^6.3.20
-        version: 6.4.2
-      karma-chrome-launcher:
-        specifier: ^3.1.1
-        version: 3.2.0
-      karma-cli:
-        specifier: ^2.0.0
-        version: 2.0.0
-      karma-esbuild:
-        specifier: ^2.2.5
-        version: 2.3.0(esbuild@0.19.8)
-      karma-jasmine:
-        specifier: ^5.0.1
-        version: 5.1.0(karma@6.4.2)
-      lint-staged:
-        specifier: ^12.5.0
-        version: 12.5.0(enquirer@2.4.1)
-      lodash:
-        specifier: ^4.17.21
-        version: 4.17.21
-      marked:
-        specifier: ^4.0.16
-        version: 4.3.0
-      minimist:
-        specifier: ^1.2.6
-        version: 1.2.8
-      postcss:
-        specifier: ^8.4.14
-        version: 8.4.32
-      prettier:
-        specifier: ^2.6.2
-        version: 2.8.8
-      puppeteer:
-        specifier: ^14.3.0
-        version: 14.4.1
-      rimraf:
-        specifier: ^3.0.2
-        version: 3.0.2
-      rollup:
-        specifier: ^2.79.1
-        version: 2.79.1
-      rollup-plugin-typescript2:
-        specifier: ^0.32.0
-        version: 0.32.1(rollup@2.79.1)(typescript@4.9.5)
-      semver:
-        specifier: ^7.3.7
-        version: 7.5.4
-      shelljs:
-        specifier: ^0.8.5
-        version: 0.8.5
-      terser:
-        specifier: ^5.14.0
-        version: 5.25.0
-      todomvc-app-css:
-        specifier: ^2.4.2
-        version: 2.4.3
-      ts-node:
-        specifier: ^10.8.1
-        version: 10.9.1(@types/node@20.10.3)(typescript@4.9.5)
-      tslib:
-        specifier: ^2.4.0
-        version: 2.6.2
-      typescript:
-        specifier: ^4.8.4
-        version: 4.9.5
-      vitest:
-        specifier: ^1.0.4
-        version: 1.0.4(@types/node@20.10.3)(jsdom@19.0.0)(terser@5.25.0)
-      yorkie:
-        specifier: ^2.0.0
-        version: 2.0.0
-
-  packages/compiler-sfc:
-    dependencies:
-      '@babel/parser':
-        specifier: ^7.23.5
-        version: 7.23.5
-      postcss:
-        specifier: ^8.4.14
-        version: 8.4.32
-      source-map:
-        specifier: ^0.6.1
-        version: 0.6.1
-    optionalDependencies:
-      prettier:
-        specifier: ^1.18.2 || ^2.0.0
-        version: 2.8.8
-    devDependencies:
-      '@babel/types':
-        specifier: ^7.23.5
-        version: 7.23.5
-      '@types/estree':
-        specifier: ^0.0.48
-        version: 0.0.48
-      '@types/hash-sum':
-        specifier: ^1.0.0
-        version: 1.0.2
-      '@types/lru-cache':
-        specifier: ^5.1.1
-        version: 5.1.1
-      '@vue/consolidate':
-        specifier: ^0.17.3
-        version: 0.17.3
-      de-indent:
-        specifier: ^1.0.2
-        version: 1.0.2
-      estree-walker:
-        specifier: ^2.0.2
-        version: 2.0.2
-      hash-sum:
-        specifier: ^2.0.0
-        version: 2.0.0
-      less:
-        specifier: ^4.1.3
-        version: 4.2.0
-      lru-cache:
-        specifier: ^5.1.1
-        version: 5.1.1
-      magic-string:
-        specifier: ^0.25.9
-        version: 0.25.9
-      merge-source-map:
-        specifier: ^1.1.0
-        version: 1.1.0
-      postcss-modules:
-        specifier: ^4.3.1
-        version: 4.3.1(postcss@8.4.32)
-      postcss-selector-parser:
-        specifier: ^6.0.10
-        version: 6.0.13
-      pug:
-        specifier: ^3.0.2
-        version: 3.0.2
-      sass:
-        specifier: ^1.52.3
-        version: 1.69.5
-      stylus:
-        specifier: ^0.58.1
-        version: 0.58.1
-
-  packages/server-renderer:
-    dependencies:
-      chalk:
-        specifier: ^4.1.2
-        version: 4.1.2
-      hash-sum:
-        specifier: ^2.0.0
-        version: 2.0.0
-      he:
-        specifier: ^1.2.0
-        version: 1.2.0
-      lodash.template:
-        specifier: ^4.5.0
-        version: 4.5.0
-      lodash.uniq:
-        specifier: ^4.5.0
-        version: 4.5.0
-      resolve:
-        specifier: ^1.22.0
-        version: 1.22.8
-      serialize-javascript:
-        specifier: ^6.0.0
-        version: 6.0.1
-      source-map:
-        specifier: 0.5.6
-        version: 0.5.6
-    devDependencies:
-      '@types/webpack':
-        specifier: ^4.41.32
-        version: 4.41.38
-      file-loader:
-        specifier: ^3.0.1
-        version: 3.0.1(webpack@4.47.0)
-      memory-fs:
-        specifier: ^0.5.0
-        version: 0.5.0
-      vue:
-        specifier: file:../..
-        version: 'file:'
-      webpack:
-        specifier: ^4.47.0
-        version: 4.47.0
-
-  packages/template-compiler:
-    dependencies:
-      de-indent:
-        specifier: ^1.0.2
-        version: 1.0.2
-      he:
-        specifier: ^1.2.0
-        version: 1.2.0
-    devDependencies:
-      vue:
-        specifier: file:../..
-        version: 'file:'
-
-packages:
-
-  /@babel/code-frame@7.23.5:
-    resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/highlight': 7.23.4
-      chalk: 2.4.2
-    dev: true
-
-  /@babel/helper-string-parser@7.23.4:
-    resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
-    engines: {node: '>=6.9.0'}
-
-  /@babel/helper-validator-identifier@7.22.20:
-    resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
-    engines: {node: '>=6.9.0'}
-
-  /@babel/highlight@7.23.4:
-    resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-validator-identifier': 7.22.20
-      chalk: 2.4.2
-      js-tokens: 4.0.0
-    dev: true
-
-  /@babel/parser@7.23.5:
-    resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-    dependencies:
-      '@babel/types': 7.23.5
-
-  /@babel/types@7.23.5:
-    resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-string-parser': 7.23.4
-      '@babel/helper-validator-identifier': 7.22.20
-      to-fast-properties: 2.0.0
-
-  /@colors/colors@1.5.0:
-    resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
-    engines: {node: '>=0.1.90'}
-    dev: true
-
-  /@cspotcode/source-map-support@0.8.1:
-    resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
-    engines: {node: '>=12'}
-    dependencies:
-      '@jridgewell/trace-mapping': 0.3.9
-    dev: true
-
-  /@esbuild/android-arm64@0.19.8:
-    resolution: {integrity: sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/android-arm@0.19.8:
-    resolution: {integrity: sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==}
-    engines: {node: '>=12'}
-    cpu: [arm]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/android-x64@0.19.8:
-    resolution: {integrity: sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/darwin-arm64@0.19.8:
-    resolution: {integrity: sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/darwin-x64@0.19.8:
-    resolution: {integrity: sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/freebsd-arm64@0.19.8:
-    resolution: {integrity: sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/freebsd-x64@0.19.8:
-    resolution: {integrity: sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-arm64@0.19.8:
-    resolution: {integrity: sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-arm@0.19.8:
-    resolution: {integrity: sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==}
-    engines: {node: '>=12'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-ia32@0.19.8:
-    resolution: {integrity: sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==}
-    engines: {node: '>=12'}
-    cpu: [ia32]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-loong64@0.19.8:
-    resolution: {integrity: sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==}
-    engines: {node: '>=12'}
-    cpu: [loong64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-mips64el@0.19.8:
-    resolution: {integrity: sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==}
-    engines: {node: '>=12'}
-    cpu: [mips64el]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-ppc64@0.19.8:
-    resolution: {integrity: sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==}
-    engines: {node: '>=12'}
-    cpu: [ppc64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-riscv64@0.19.8:
-    resolution: {integrity: sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==}
-    engines: {node: '>=12'}
-    cpu: [riscv64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-s390x@0.19.8:
-    resolution: {integrity: sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==}
-    engines: {node: '>=12'}
-    cpu: [s390x]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/linux-x64@0.19.8:
-    resolution: {integrity: sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/netbsd-x64@0.19.8:
-    resolution: {integrity: sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [netbsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/openbsd-x64@0.19.8:
-    resolution: {integrity: sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [openbsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/sunos-x64@0.19.8:
-    resolution: {integrity: sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [sunos]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/win32-arm64@0.19.8:
-    resolution: {integrity: sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/win32-ia32@0.19.8:
-    resolution: {integrity: sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==}
-    engines: {node: '>=12'}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@esbuild/win32-x64@0.19.8:
-    resolution: {integrity: sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@hutson/parse-repository-url@3.0.2:
-    resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@jest/schemas@29.6.3:
-    resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    dependencies:
-      '@sinclair/typebox': 0.27.8
-    dev: true
-
-  /@jridgewell/gen-mapping@0.3.3:
-    resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      '@jridgewell/set-array': 1.1.2
-      '@jridgewell/sourcemap-codec': 1.4.15
-      '@jridgewell/trace-mapping': 0.3.20
-    dev: true
-
-  /@jridgewell/resolve-uri@3.1.1:
-    resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==}
-    engines: {node: '>=6.0.0'}
-    dev: true
-
-  /@jridgewell/set-array@1.1.2:
-    resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
-    engines: {node: '>=6.0.0'}
-    dev: true
-
-  /@jridgewell/source-map@0.3.5:
-    resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==}
-    dependencies:
-      '@jridgewell/gen-mapping': 0.3.3
-      '@jridgewell/trace-mapping': 0.3.20
-    dev: true
-
-  /@jridgewell/sourcemap-codec@1.4.15:
-    resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
-    dev: true
-
-  /@jridgewell/trace-mapping@0.3.20:
-    resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==}
-    dependencies:
-      '@jridgewell/resolve-uri': 3.1.1
-      '@jridgewell/sourcemap-codec': 1.4.15
-    dev: true
-
-  /@jridgewell/trace-mapping@0.3.9:
-    resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
-    dependencies:
-      '@jridgewell/resolve-uri': 3.1.1
-      '@jridgewell/sourcemap-codec': 1.4.15
-    dev: true
-
-  /@microsoft/api-extractor-model@7.28.2(@types/node@20.10.3):
-    resolution: {integrity: sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==}
-    dependencies:
-      '@microsoft/tsdoc': 0.14.2
-      '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.61.0(@types/node@20.10.3)
-    transitivePeerDependencies:
-      - '@types/node'
-    dev: true
-
-  /@microsoft/api-extractor@7.38.4(@types/node@20.10.3):
-    resolution: {integrity: sha512-0SW3Of6os4bAYlHdiD1hJx/ygXr7vRZi92E1pREufNERH87aZ0B9Vhku/4Mj2Oxp58gyV5d18t7uZold6HCSEw==}
-    hasBin: true
-    dependencies:
-      '@microsoft/api-extractor-model': 7.28.2(@types/node@20.10.3)
-      '@microsoft/tsdoc': 0.14.2
-      '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.61.0(@types/node@20.10.3)
-      '@rushstack/rig-package': 0.5.1
-      '@rushstack/ts-command-line': 4.17.1
-      colors: 1.2.5
-      lodash: 4.17.21
-      resolve: 1.22.8
-      semver: 7.5.4
-      source-map: 0.6.1
-      typescript: 5.0.4
-    transitivePeerDependencies:
-      - '@types/node'
-    dev: true
-
-  /@microsoft/tsdoc-config@0.16.2:
-    resolution: {integrity: sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==}
-    dependencies:
-      '@microsoft/tsdoc': 0.14.2
-      ajv: 6.12.6
-      jju: 1.4.0
-      resolve: 1.19.0
-    dev: true
-
-  /@microsoft/tsdoc@0.14.2:
-    resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==}
-    dev: true
-
-  /@rollup/plugin-alias@3.1.9(rollup@2.79.1):
-    resolution: {integrity: sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==}
-    engines: {node: '>=8.0.0'}
-    peerDependencies:
-      rollup: ^1.20.0||^2.0.0
-    dependencies:
-      rollup: 2.79.1
-      slash: 3.0.0
-    dev: true
-
-  /@rollup/plugin-commonjs@22.0.2(rollup@2.79.1):
-    resolution: {integrity: sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg==}
-    engines: {node: '>= 12.0.0'}
-    peerDependencies:
-      rollup: ^2.68.0
-    dependencies:
-      '@rollup/pluginutils': 3.1.0(rollup@2.79.1)
-      commondir: 1.0.1
-      estree-walker: 2.0.2
-      glob: 7.2.3
-      is-reference: 1.2.1
-      magic-string: 0.25.9
-      resolve: 1.22.8
-      rollup: 2.79.1
-    dev: true
-
-  /@rollup/plugin-node-resolve@13.3.0(rollup@2.79.1):
-    resolution: {integrity: sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==}
-    engines: {node: '>= 10.0.0'}
-    peerDependencies:
-      rollup: ^2.42.0
-    dependencies:
-      '@rollup/pluginutils': 3.1.0(rollup@2.79.1)
-      '@types/resolve': 1.17.1
-      deepmerge: 4.3.1
-      is-builtin-module: 3.2.1
-      is-module: 1.0.0
-      resolve: 1.22.8
-      rollup: 2.79.1
-    dev: true
-
-  /@rollup/plugin-replace@4.0.0(rollup@2.79.1):
-    resolution: {integrity: sha512-+rumQFiaNac9y64OHtkHGmdjm7us9bo1PlbgQfdihQtuNxzjpaB064HbRnewUOggLQxVCCyINfStkgmBeQpv1g==}
-    peerDependencies:
-      rollup: ^1.20.0 || ^2.0.0
-    dependencies:
-      '@rollup/pluginutils': 3.1.0(rollup@2.79.1)
-      magic-string: 0.25.9
-      rollup: 2.79.1
-    dev: true
-
-  /@rollup/pluginutils@3.1.0(rollup@2.79.1):
-    resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
-    engines: {node: '>= 8.0.0'}
-    peerDependencies:
-      rollup: ^1.20.0||^2.0.0
-    dependencies:
-      '@types/estree': 0.0.39
-      estree-walker: 1.0.1
-      picomatch: 2.3.1
-      rollup: 2.79.1
-    dev: true
-
-  /@rollup/pluginutils@4.2.1:
-    resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
-    engines: {node: '>= 8.0.0'}
-    dependencies:
-      estree-walker: 2.0.2
-      picomatch: 2.3.1
-    dev: true
-
-  /@rollup/rollup-android-arm-eabi@4.6.1:
-    resolution: {integrity: sha512-0WQ0ouLejaUCRsL93GD4uft3rOmB8qoQMU05Kb8CmMtMBe7XUDLAltxVZI1q6byNqEtU7N1ZX1Vw5lIpgulLQA==}
-    cpu: [arm]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-android-arm64@4.6.1:
-    resolution: {integrity: sha512-1TKm25Rn20vr5aTGGZqo6E4mzPicCUD79k17EgTLAsXc1zysyi4xXKACfUbwyANEPAEIxkzwue6JZ+stYzWUTA==}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-darwin-arm64@4.6.1:
-    resolution: {integrity: sha512-cEXJQY/ZqMACb+nxzDeX9IPLAg7S94xouJJCNVE5BJM8JUEP4HeTF+ti3cmxWeSJo+5D+o8Tc0UAWUkfENdeyw==}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-darwin-x64@4.6.1:
-    resolution: {integrity: sha512-LoSU9Xu56isrkV2jLldcKspJ7sSXmZWkAxg7sW/RfF7GS4F5/v4EiqKSMCFbZtDu2Nc1gxxFdQdKwkKS4rwxNg==}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-arm-gnueabihf@4.6.1:
-    resolution: {integrity: sha512-EfI3hzYAy5vFNDqpXsNxXcgRDcFHUWSx5nnRSCKwXuQlI5J9dD84g2Usw81n3FLBNsGCegKGwwTVsSKK9cooSQ==}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-arm64-gnu@4.6.1:
-    resolution: {integrity: sha512-9lhc4UZstsegbNLhH0Zu6TqvDfmhGzuCWtcTFXY10VjLLUe4Mr0Ye2L3rrtHaDd/J5+tFMEuo5LTCSCMXWfUKw==}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-arm64-musl@4.6.1:
-    resolution: {integrity: sha512-FfoOK1yP5ksX3wwZ4Zk1NgyGHZyuRhf99j64I5oEmirV8EFT7+OhUZEnP+x17lcP/QHJNWGsoJwrz4PJ9fBEXw==}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-x64-gnu@4.6.1:
-    resolution: {integrity: sha512-DNGZvZDO5YF7jN5fX8ZqmGLjZEXIJRdJEdTFMhiyXqyXubBa0WVLDWSNlQ5JR2PNgDbEV1VQowhVRUh+74D+RA==}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-linux-x64-musl@4.6.1:
-    resolution: {integrity: sha512-RkJVNVRM+piYy87HrKmhbexCHg3A6Z6MU0W9GHnJwBQNBeyhCJG9KDce4SAMdicQnpURggSvtbGo9xAWOfSvIQ==}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-win32-arm64-msvc@4.6.1:
-    resolution: {integrity: sha512-v2FVT6xfnnmTe3W9bJXl6r5KwJglMK/iRlkKiIFfO6ysKs0rDgz7Cwwf3tjldxQUrHL9INT/1r4VA0n9L/F1vQ==}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-win32-ia32-msvc@4.6.1:
-    resolution: {integrity: sha512-YEeOjxRyEjqcWphH9dyLbzgkF8wZSKAKUkldRY6dgNR5oKs2LZazqGB41cWJ4Iqqcy9/zqYgmzBkRoVz3Q9MLw==}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rollup/rollup-win32-x64-msvc@4.6.1:
-    resolution: {integrity: sha512-0zfTlFAIhgz8V2G8STq8toAjsYYA6eci1hnXuyOTUFnymrtJwnS6uGKiv3v5UrPZkBlamLvrLV2iiaeqCKzb0A==}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /@rushstack/node-core-library@3.61.0(@types/node@20.10.3):
-    resolution: {integrity: sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==}
-    peerDependencies:
-      '@types/node': '*'
-    peerDependenciesMeta:
-      '@types/node':
-        optional: true
-    dependencies:
-      '@types/node': 20.10.3
-      colors: 1.2.5
-      fs-extra: 7.0.1
-      import-lazy: 4.0.0
-      jju: 1.4.0
-      resolve: 1.22.8
-      semver: 7.5.4
-      z-schema: 5.0.5
-    dev: true
-
-  /@rushstack/rig-package@0.5.1:
-    resolution: {integrity: sha512-pXRYSe29TjRw7rqxD4WS3HN/sRSbfr+tJs4a9uuaSIBAITbUggygdhuG0VrO0EO+QqH91GhYMN4S6KRtOEmGVA==}
-    dependencies:
-      resolve: 1.22.8
-      strip-json-comments: 3.1.1
-    dev: true
-
-  /@rushstack/ts-command-line@4.17.1:
-    resolution: {integrity: sha512-2jweO1O57BYP5qdBGl6apJLB+aRIn5ccIRTPDyULh0KMwVzFqWtw6IZWt1qtUoZD/pD2RNkIOosH6Cq45rIYeg==}
-    dependencies:
-      '@types/argparse': 1.0.38
-      argparse: 1.0.10
-      colors: 1.2.5
-      string-argv: 0.3.2
-    dev: true
-
-  /@sinclair/typebox@0.27.8:
-    resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
-    dev: true
-
-  /@socket.io/component-emitter@3.1.0:
-    resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==}
-    dev: true
-
-  /@tootallnate/once@2.0.0:
-    resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
-    engines: {node: '>= 10'}
-    dev: true
-
-  /@tsconfig/node10@1.0.9:
-    resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
-    dev: true
-
-  /@tsconfig/node12@1.0.11:
-    resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
-    dev: true
-
-  /@tsconfig/node14@1.0.3:
-    resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
-    dev: true
-
-  /@tsconfig/node16@1.0.4:
-    resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
-    dev: true
-
-  /@types/argparse@1.0.38:
-    resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==}
-    dev: true
-
-  /@types/cookie@0.4.1:
-    resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
-    dev: true
-
-  /@types/cors@2.8.17:
-    resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
-    dependencies:
-      '@types/node': 20.10.3
-    dev: true
-
-  /@types/estree@0.0.39:
-    resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
-    dev: true
-
-  /@types/estree@0.0.48:
-    resolution: {integrity: sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==}
-    dev: true
-
-  /@types/hash-sum@1.0.2:
-    resolution: {integrity: sha512-UP28RddqY8xcU0SCEp9YKutQICXpaAq9N8U2klqF5hegGha7KzTOL8EdhIIV3bOSGBzjEpN9bU/d+nNZBdJYVw==}
-    dev: true
-
-  /@types/he@1.2.3:
-    resolution: {integrity: sha512-q67/qwlxblDzEDvzHhVkwc1gzVWxaNxeyHUBF4xElrvjL11O+Ytze+1fGpBHlr/H9myiBUaUXNnNPmBHxxfAcA==}
-    dev: true
-
-  /@types/lru-cache@5.1.1:
-    resolution: {integrity: sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==}
-    dev: true
-
-  /@types/minimist@1.2.5:
-    resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
-    dev: true
-
-  /@types/node@20.10.3:
-    resolution: {integrity: sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==}
-    dependencies:
-      undici-types: 5.26.5
-    dev: true
-
-  /@types/normalize-package-data@2.4.4:
-    resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
-    dev: true
-
-  /@types/resolve@1.17.1:
-    resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
-    dependencies:
-      '@types/node': 20.10.3
-    dev: true
-
-  /@types/source-list-map@0.1.6:
-    resolution: {integrity: sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==}
-    dev: true
-
-  /@types/tapable@1.0.12:
-    resolution: {integrity: sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==}
-    dev: true
-
-  /@types/uglify-js@3.17.4:
-    resolution: {integrity: sha512-Hm/T0kV3ywpJyMGNbsItdivRhYNCQQf1IIsYsXnoVPES4t+FMLyDe0/K+Ea7ahWtMtSNb22ZdY7MIyoD9rqARg==}
-    dependencies:
-      source-map: 0.6.1
-    dev: true
-
-  /@types/webpack-sources@3.2.3:
-    resolution: {integrity: sha512-4nZOdMwSPHZ4pTEZzSp0AsTM4K7Qmu40UKW4tJDiOVs20UzYF9l+qUe4s0ftfN0pin06n+5cWWDJXH+sbhAiDw==}
-    dependencies:
-      '@types/node': 20.10.3
-      '@types/source-list-map': 0.1.6
-      source-map: 0.7.4
-    dev: true
-
-  /@types/webpack@4.41.38:
-    resolution: {integrity: sha512-oOW7E931XJU1mVfCnxCVgv8GLFL768pDO5u2Gzk82i8yTIgX6i7cntyZOkZYb/JtYM8252SN9bQp9tgkVDSsRw==}
-    dependencies:
-      '@types/node': 20.10.3
-      '@types/tapable': 1.0.12
-      '@types/uglify-js': 3.17.4
-      '@types/webpack-sources': 3.2.3
-      anymatch: 3.1.3
-      source-map: 0.6.1
-    dev: true
-
-  /@types/yauzl@2.10.3:
-    resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
-    requiresBuild: true
-    dependencies:
-      '@types/node': 20.10.3
-    dev: true
-    optional: true
-
-  /@vitest/expect@1.0.4:
-    resolution: {integrity: sha512-/NRN9N88qjg3dkhmFcCBwhn/Ie4h064pY3iv7WLRsDJW7dXnEgeoa8W9zy7gIPluhz6CkgqiB3HmpIXgmEY5dQ==}
-    dependencies:
-      '@vitest/spy': 1.0.4
-      '@vitest/utils': 1.0.4
-      chai: 4.3.10
-    dev: true
-
-  /@vitest/runner@1.0.4:
-    resolution: {integrity: sha512-rhOQ9FZTEkV41JWXozFM8YgOqaG9zA7QXbhg5gy6mFOVqh4PcupirIJ+wN7QjeJt8S8nJRYuZH1OjJjsbxAXTQ==}
-    dependencies:
-      '@vitest/utils': 1.0.4
-      p-limit: 5.0.0
-      pathe: 1.1.1
-    dev: true
-
-  /@vitest/snapshot@1.0.4:
-    resolution: {integrity: sha512-vkfXUrNyNRA/Gzsp2lpyJxh94vU2OHT1amoD6WuvUAA12n32xeVZQ0KjjQIf8F6u7bcq2A2k969fMVxEsxeKYA==}
-    dependencies:
-      magic-string: 0.30.5
-      pathe: 1.1.1
-      pretty-format: 29.7.0
-    dev: true
-
-  /@vitest/spy@1.0.4:
-    resolution: {integrity: sha512-9ojTFRL1AJVh0hvfzAQpm0QS6xIS+1HFIw94kl/1ucTfGCaj1LV/iuJU4Y6cdR03EzPDygxTHwE1JOm+5RCcvA==}
-    dependencies:
-      tinyspy: 2.2.0
-    dev: true
-
-  /@vitest/utils@1.0.4:
-    resolution: {integrity: sha512-gsswWDXxtt0QvtK/y/LWukN7sGMYmnCcv1qv05CsY6cU/Y1zpGX1QuvLs+GO1inczpE6Owixeel3ShkjhYtGfA==}
-    dependencies:
-      diff-sequences: 29.6.3
-      loupe: 2.3.7
-      pretty-format: 29.7.0
-    dev: true
-
-  /@vue/consolidate@0.17.3:
-    resolution: {integrity: sha512-nl0SWcTMzaaTnJ5G6V8VlMDA1CVVrNnaQKF1aBZU3kXtjgU9jtHMsEAsgjoRUx+T0EVJk9TgbmxGhK3pOk22zw==}
-    engines: {node: '>= 0.12.0'}
-    dev: true
-
-  /@webassemblyjs/ast@1.9.0:
-    resolution: {integrity: sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==}
-    dependencies:
-      '@webassemblyjs/helper-module-context': 1.9.0
-      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
-      '@webassemblyjs/wast-parser': 1.9.0
-    dev: true
-
-  /@webassemblyjs/floating-point-hex-parser@1.9.0:
-    resolution: {integrity: sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==}
-    dev: true
-
-  /@webassemblyjs/helper-api-error@1.9.0:
-    resolution: {integrity: sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==}
-    dev: true
-
-  /@webassemblyjs/helper-buffer@1.9.0:
-    resolution: {integrity: sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==}
-    dev: true
-
-  /@webassemblyjs/helper-code-frame@1.9.0:
-    resolution: {integrity: sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==}
-    dependencies:
-      '@webassemblyjs/wast-printer': 1.9.0
-    dev: true
-
-  /@webassemblyjs/helper-fsm@1.9.0:
-    resolution: {integrity: sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==}
-    dev: true
-
-  /@webassemblyjs/helper-module-context@1.9.0:
-    resolution: {integrity: sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-    dev: true
-
-  /@webassemblyjs/helper-wasm-bytecode@1.9.0:
-    resolution: {integrity: sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==}
-    dev: true
-
-  /@webassemblyjs/helper-wasm-section@1.9.0:
-    resolution: {integrity: sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-buffer': 1.9.0
-      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
-      '@webassemblyjs/wasm-gen': 1.9.0
-    dev: true
-
-  /@webassemblyjs/ieee754@1.9.0:
-    resolution: {integrity: sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==}
-    dependencies:
-      '@xtuc/ieee754': 1.2.0
-    dev: true
-
-  /@webassemblyjs/leb128@1.9.0:
-    resolution: {integrity: sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==}
-    dependencies:
-      '@xtuc/long': 4.2.2
-    dev: true
-
-  /@webassemblyjs/utf8@1.9.0:
-    resolution: {integrity: sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==}
-    dev: true
-
-  /@webassemblyjs/wasm-edit@1.9.0:
-    resolution: {integrity: sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-buffer': 1.9.0
-      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
-      '@webassemblyjs/helper-wasm-section': 1.9.0
-      '@webassemblyjs/wasm-gen': 1.9.0
-      '@webassemblyjs/wasm-opt': 1.9.0
-      '@webassemblyjs/wasm-parser': 1.9.0
-      '@webassemblyjs/wast-printer': 1.9.0
-    dev: true
-
-  /@webassemblyjs/wasm-gen@1.9.0:
-    resolution: {integrity: sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
-      '@webassemblyjs/ieee754': 1.9.0
-      '@webassemblyjs/leb128': 1.9.0
-      '@webassemblyjs/utf8': 1.9.0
-    dev: true
-
-  /@webassemblyjs/wasm-opt@1.9.0:
-    resolution: {integrity: sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-buffer': 1.9.0
-      '@webassemblyjs/wasm-gen': 1.9.0
-      '@webassemblyjs/wasm-parser': 1.9.0
-    dev: true
-
-  /@webassemblyjs/wasm-parser@1.9.0:
-    resolution: {integrity: sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-api-error': 1.9.0
-      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
-      '@webassemblyjs/ieee754': 1.9.0
-      '@webassemblyjs/leb128': 1.9.0
-      '@webassemblyjs/utf8': 1.9.0
-    dev: true
-
-  /@webassemblyjs/wast-parser@1.9.0:
-    resolution: {integrity: sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/floating-point-hex-parser': 1.9.0
-      '@webassemblyjs/helper-api-error': 1.9.0
-      '@webassemblyjs/helper-code-frame': 1.9.0
-      '@webassemblyjs/helper-fsm': 1.9.0
-      '@xtuc/long': 4.2.2
-    dev: true
-
-  /@webassemblyjs/wast-printer@1.9.0:
-    resolution: {integrity: sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/wast-parser': 1.9.0
-      '@xtuc/long': 4.2.2
-    dev: true
-
-  /@xtuc/ieee754@1.2.0:
-    resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
-    dev: true
-
-  /@xtuc/long@4.2.2:
-    resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
-    dev: true
-
-  /JSONStream@1.3.5:
-    resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
-    hasBin: true
-    dependencies:
-      jsonparse: 1.3.1
-      through: 2.3.8
-    dev: true
-
-  /abab@2.0.6:
-    resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
-    deprecated: Use your platform's native atob() and btoa() methods instead
-    dev: true
-
-  /accepts@1.3.8:
-    resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
-    engines: {node: '>= 0.6'}
-    dependencies:
-      mime-types: 2.1.35
-      negotiator: 0.6.3
-    dev: true
-
-  /acorn-globals@6.0.0:
-    resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==}
-    dependencies:
-      acorn: 7.4.1
-      acorn-walk: 7.2.0
-    dev: true
-
-  /acorn-walk@7.2.0:
-    resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
-    engines: {node: '>=0.4.0'}
-    dev: true
-
-  /acorn-walk@8.3.1:
-    resolution: {integrity: sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==}
-    engines: {node: '>=0.4.0'}
-    dev: true
-
-  /acorn@6.4.2:
-    resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
-    dev: true
-
-  /acorn@7.4.1:
-    resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
-    dev: true
-
-  /acorn@8.11.2:
-    resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
-    dev: true
-
-  /add-stream@1.0.0:
-    resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==}
-    dev: true
-
-  /agent-base@6.0.2:
-    resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
-    engines: {node: '>= 6.0.0'}
-    dependencies:
-      debug: 4.3.4(supports-color@9.4.0)
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /aggregate-error@3.1.0:
-    resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
-    engines: {node: '>=8'}
-    dependencies:
-      clean-stack: 2.2.0
-      indent-string: 4.0.0
-    dev: true
-
-  /ajv-errors@1.0.1(ajv@6.12.6):
-    resolution: {integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==}
-    peerDependencies:
-      ajv: '>=5.0.0'
-    dependencies:
-      ajv: 6.12.6
-    dev: true
-
-  /ajv-keywords@3.5.2(ajv@6.12.6):
-    resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
-    peerDependencies:
-      ajv: ^6.9.1
-    dependencies:
-      ajv: 6.12.6
-    dev: true
-
-  /ajv@6.12.6:
-    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
-    dependencies:
-      fast-deep-equal: 3.1.3
-      fast-json-stable-stringify: 2.1.0
-      json-schema-traverse: 0.4.1
-      uri-js: 4.4.1
-    dev: true
-
-  /ansi-colors@4.1.3:
-    resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /ansi-escapes@4.3.2:
-    resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      type-fest: 0.21.3
-    dev: true
-
-  /ansi-regex@5.0.1:
-    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /ansi-regex@6.0.1:
-    resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /ansi-styles@3.2.1:
-    resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
-    engines: {node: '>=4'}
-    dependencies:
-      color-convert: 1.9.3
-    dev: true
-
-  /ansi-styles@4.3.0:
-    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
-    engines: {node: '>=8'}
-    dependencies:
-      color-convert: 2.0.1
-
-  /ansi-styles@5.2.0:
-    resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /ansi-styles@6.2.1:
-    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /anymatch@2.0.0:
-    resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==}
-    requiresBuild: true
-    dependencies:
-      micromatch: 3.1.10
-      normalize-path: 2.1.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-    optional: true
-
-  /anymatch@3.1.3:
-    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
-    engines: {node: '>= 8'}
-    dependencies:
-      normalize-path: 3.0.0
-      picomatch: 2.3.1
-    dev: true
-
-  /aproba@1.2.0:
-    resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==}
-    dev: true
-
-  /arg@4.1.3:
-    resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
-    dev: true
-
-  /argparse@1.0.10:
-    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
-    dependencies:
-      sprintf-js: 1.0.3
-    dev: true
-
-  /arr-diff@4.0.0:
-    resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /arr-flatten@1.1.0:
-    resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /arr-union@3.1.0:
-    resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /array-ify@1.0.0:
-    resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==}
-    dev: true
-
-  /array-unique@0.3.2:
-    resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /arrify@1.0.1:
-    resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /asap@2.0.6:
-    resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
-    dev: true
-
-  /asn1.js@5.4.1:
-    resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==}
-    dependencies:
-      bn.js: 4.12.0
-      inherits: 2.0.4
-      minimalistic-assert: 1.0.1
-      safer-buffer: 2.1.2
-    dev: true
-
-  /assert-never@1.2.1:
-    resolution: {integrity: sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==}
-    dev: true
-
-  /assert@1.5.1:
-    resolution: {integrity: sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==}
-    dependencies:
-      object.assign: 4.1.5
-      util: 0.10.4
-    dev: true
-
-  /assertion-error@1.1.0:
-    resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
-    dev: true
-
-  /assign-symbols@1.0.0:
-    resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /astral-regex@2.0.0:
-    resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /async-each@1.0.6:
-    resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==}
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /asynckit@0.4.0:
-    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
-    dev: true
-
-  /atob@2.1.2:
-    resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==}
-    engines: {node: '>= 4.5.0'}
-    hasBin: true
-    dev: true
-
-  /babel-walk@3.0.0-canary-5:
-    resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==}
-    engines: {node: '>= 10.0.0'}
-    dependencies:
-      '@babel/types': 7.23.5
-    dev: true
-
-  /balanced-match@1.0.2:
-    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
-    dev: true
-
-  /base64-js@1.5.1:
-    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
-    dev: true
-
-  /base64id@2.0.0:
-    resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
-    engines: {node: ^4.5.0 || >= 5.9}
-    dev: true
-
-  /base@0.11.2:
-    resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      cache-base: 1.0.1
-      class-utils: 0.3.6
-      component-emitter: 1.3.1
-      define-property: 1.0.0
-      isobject: 3.0.1
-      mixin-deep: 1.3.2
-      pascalcase: 0.1.1
-    dev: true
-
-  /big.js@5.2.2:
-    resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
-    dev: true
-
-  /binary-extensions@1.13.1:
-    resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==}
-    engines: {node: '>=0.10.0'}
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /binary-extensions@2.2.0:
-    resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /bindings@1.5.0:
-    resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
-    requiresBuild: true
-    dependencies:
-      file-uri-to-path: 1.0.0
-    dev: true
-    optional: true
-
-  /bl@4.1.0:
-    resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
-    dependencies:
-      buffer: 5.7.1
-      inherits: 2.0.4
-      readable-stream: 3.6.2
-    dev: true
-
-  /bluebird@3.7.2:
-    resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
-    dev: true
-
-  /bn.js@4.12.0:
-    resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==}
-    dev: true
-
-  /bn.js@5.2.1:
-    resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==}
-    dev: true
-
-  /body-parser@1.20.2:
-    resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
-    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
-    dependencies:
-      bytes: 3.1.2
-      content-type: 1.0.5
-      debug: 2.6.9
-      depd: 2.0.0
-      destroy: 1.2.0
-      http-errors: 2.0.0
-      iconv-lite: 0.4.24
-      on-finished: 2.4.1
-      qs: 6.11.0
-      raw-body: 2.5.2
-      type-is: 1.6.18
-      unpipe: 1.0.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /brace-expansion@1.1.11:
-    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
-    dependencies:
-      balanced-match: 1.0.2
-      concat-map: 0.0.1
-    dev: true
-
-  /braces@2.3.2:
-    resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-flatten: 1.1.0
-      array-unique: 0.3.2
-      extend-shallow: 2.0.1
-      fill-range: 4.0.0
-      isobject: 3.0.1
-      repeat-element: 1.1.4
-      snapdragon: 0.8.2
-      snapdragon-node: 2.1.1
-      split-string: 3.1.0
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /braces@3.0.2:
-    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
-    engines: {node: '>=8'}
-    dependencies:
-      fill-range: 7.0.1
-    dev: true
-
-  /brorand@1.1.0:
-    resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==}
-    dev: true
-
-  /browser-process-hrtime@1.0.0:
-    resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
-    dev: true
-
-  /browserify-aes@1.2.0:
-    resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==}
-    dependencies:
-      buffer-xor: 1.0.3
-      cipher-base: 1.0.4
-      create-hash: 1.2.0
-      evp_bytestokey: 1.0.3
-      inherits: 2.0.4
-      safe-buffer: 5.2.1
-    dev: true
-
-  /browserify-cipher@1.0.1:
-    resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==}
-    dependencies:
-      browserify-aes: 1.2.0
-      browserify-des: 1.0.2
-      evp_bytestokey: 1.0.3
-    dev: true
-
-  /browserify-des@1.0.2:
-    resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==}
-    dependencies:
-      cipher-base: 1.0.4
-      des.js: 1.1.0
-      inherits: 2.0.4
-      safe-buffer: 5.2.1
-    dev: true
-
-  /browserify-rsa@4.1.0:
-    resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==}
-    dependencies:
-      bn.js: 5.2.1
-      randombytes: 2.1.0
-    dev: true
-
-  /browserify-sign@4.2.2:
-    resolution: {integrity: sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==}
-    engines: {node: '>= 4'}
-    dependencies:
-      bn.js: 5.2.1
-      browserify-rsa: 4.1.0
-      create-hash: 1.2.0
-      create-hmac: 1.1.7
-      elliptic: 6.5.4
-      inherits: 2.0.4
-      parse-asn1: 5.1.6
-      readable-stream: 3.6.2
-      safe-buffer: 5.2.1
-    dev: true
-
-  /browserify-zlib@0.2.0:
-    resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==}
-    dependencies:
-      pako: 1.0.11
-    dev: true
-
-  /buffer-crc32@0.2.13:
-    resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
-    dev: true
-
-  /buffer-from@1.1.2:
-    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
-    dev: true
-
-  /buffer-xor@1.0.3:
-    resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==}
-    dev: true
-
-  /buffer@4.9.2:
-    resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==}
-    dependencies:
-      base64-js: 1.5.1
-      ieee754: 1.2.1
-      isarray: 1.0.0
-    dev: true
-
-  /buffer@5.7.1:
-    resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
-    dependencies:
-      base64-js: 1.5.1
-      ieee754: 1.2.1
-    dev: true
-
-  /builtin-modules@3.3.0:
-    resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /builtin-status-codes@3.0.0:
-    resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==}
-    dev: true
-
-  /bytes@3.1.2:
-    resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
-    engines: {node: '>= 0.8'}
-    dev: true
-
-  /cac@6.7.14:
-    resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /cacache@12.0.4:
-    resolution: {integrity: sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==}
-    dependencies:
-      bluebird: 3.7.2
-      chownr: 1.1.4
-      figgy-pudding: 3.5.2
-      glob: 7.2.3
-      graceful-fs: 4.2.11
-      infer-owner: 1.0.4
-      lru-cache: 5.1.1
-      mississippi: 3.0.0
-      mkdirp: 0.5.6
-      move-concurrently: 1.0.1
-      promise-inflight: 1.0.1(bluebird@3.7.2)
-      rimraf: 2.7.1
-      ssri: 6.0.2
-      unique-filename: 1.1.1
-      y18n: 4.0.3
-    dev: true
-
-  /cache-base@1.0.1:
-    resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      collection-visit: 1.0.0
-      component-emitter: 1.3.1
-      get-value: 2.0.6
-      has-value: 1.0.0
-      isobject: 3.0.1
-      set-value: 2.0.1
-      to-object-path: 0.3.0
-      union-value: 1.0.1
-      unset-value: 1.0.0
-    dev: true
-
-  /call-bind@1.0.5:
-    resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
-    dependencies:
-      function-bind: 1.1.2
-      get-intrinsic: 1.2.2
-      set-function-length: 1.1.1
-    dev: true
-
-  /camelcase-keys@6.2.2:
-    resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==}
-    engines: {node: '>=8'}
-    dependencies:
-      camelcase: 5.3.1
-      map-obj: 4.3.0
-      quick-lru: 4.0.1
-    dev: true
-
-  /camelcase@5.3.1:
-    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /chai@4.3.10:
-    resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==}
-    engines: {node: '>=4'}
-    dependencies:
-      assertion-error: 1.1.0
-      check-error: 1.0.3
-      deep-eql: 4.1.3
-      get-func-name: 2.0.2
-      loupe: 2.3.7
-      pathval: 1.1.1
-      type-detect: 4.0.8
-    dev: true
-
-  /chalk@2.4.2:
-    resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
-    engines: {node: '>=4'}
-    dependencies:
-      ansi-styles: 3.2.1
-      escape-string-regexp: 1.0.5
-      supports-color: 5.5.0
-    dev: true
-
-  /chalk@4.1.2:
-    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
-    engines: {node: '>=10'}
-    dependencies:
-      ansi-styles: 4.3.0
-      supports-color: 7.2.0
-
-  /character-parser@2.2.0:
-    resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==}
-    dependencies:
-      is-regex: 1.1.4
-    dev: true
-
-  /check-error@1.0.3:
-    resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
-    dependencies:
-      get-func-name: 2.0.2
-    dev: true
-
-  /chokidar@2.1.8:
-    resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==}
-    deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies
-    requiresBuild: true
-    dependencies:
-      anymatch: 2.0.0
-      async-each: 1.0.6
-      braces: 2.3.2
-      glob-parent: 3.1.0
-      inherits: 2.0.4
-      is-binary-path: 1.0.1
-      is-glob: 4.0.3
-      normalize-path: 3.0.0
-      path-is-absolute: 1.0.1
-      readdirp: 2.2.1
-      upath: 1.2.0
-    optionalDependencies:
-      fsevents: 1.2.13
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-    optional: true
-
-  /chokidar@3.5.3:
-    resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
-    engines: {node: '>= 8.10.0'}
-    dependencies:
-      anymatch: 3.1.3
-      braces: 3.0.2
-      glob-parent: 5.1.2
-      is-binary-path: 2.1.0
-      is-glob: 4.0.3
-      normalize-path: 3.0.0
-      readdirp: 3.6.0
-    optionalDependencies:
-      fsevents: 2.3.3
-    dev: true
-
-  /chownr@1.1.4:
-    resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
-    dev: true
-
-  /chrome-trace-event@1.0.3:
-    resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
-    engines: {node: '>=6.0'}
-    dev: true
-
-  /ci-info@1.6.0:
-    resolution: {integrity: sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==}
-    dev: true
-
-  /cipher-base@1.0.4:
-    resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==}
-    dependencies:
-      inherits: 2.0.4
-      safe-buffer: 5.2.1
-    dev: true
-
-  /class-utils@0.3.6:
-    resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-union: 3.1.0
-      define-property: 0.2.5
-      isobject: 3.0.1
-      static-extend: 0.1.2
-    dev: true
-
-  /clean-stack@2.2.0:
-    resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /cli-cursor@3.1.0:
-    resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
-    engines: {node: '>=8'}
-    dependencies:
-      restore-cursor: 3.1.0
-    dev: true
-
-  /cli-truncate@2.1.0:
-    resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
-    engines: {node: '>=8'}
-    dependencies:
-      slice-ansi: 3.0.0
-      string-width: 4.2.3
-    dev: true
-
-  /cli-truncate@3.1.0:
-    resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-    dependencies:
-      slice-ansi: 5.0.0
-      string-width: 5.1.2
-    dev: true
-
-  /cliui@7.0.4:
-    resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
-    dependencies:
-      string-width: 4.2.3
-      strip-ansi: 6.0.1
-      wrap-ansi: 7.0.0
-    dev: true
-
-  /collection-visit@1.0.0:
-    resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      map-visit: 1.0.0
-      object-visit: 1.0.1
-    dev: true
-
-  /color-convert@1.9.3:
-    resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
-    dependencies:
-      color-name: 1.1.3
-    dev: true
-
-  /color-convert@2.0.1:
-    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
-    engines: {node: '>=7.0.0'}
-    dependencies:
-      color-name: 1.1.4
-
-  /color-name@1.1.3:
-    resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
-    dev: true
-
-  /color-name@1.1.4:
-    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
-
-  /colorette@2.0.20:
-    resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
-    dev: true
-
-  /colors@1.2.5:
-    resolution: {integrity: sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==}
-    engines: {node: '>=0.1.90'}
-    dev: true
-
-  /combined-stream@1.0.8:
-    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      delayed-stream: 1.0.0
-    dev: true
-
-  /commander@2.20.3:
-    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
-    dev: true
-
-  /commander@9.5.0:
-    resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
-    engines: {node: ^12.20.0 || >=14}
-    dev: true
-
-  /commondir@1.0.1:
-    resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
-    dev: true
-
-  /compare-func@2.0.0:
-    resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==}
-    dependencies:
-      array-ify: 1.0.0
-      dot-prop: 5.3.0
-    dev: true
-
-  /component-emitter@1.3.1:
-    resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
-    dev: true
-
-  /concat-map@0.0.1:
-    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
-    dev: true
-
-  /concat-stream@1.6.2:
-    resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
-    engines: {'0': node >= 0.8}
-    dependencies:
-      buffer-from: 1.1.2
-      inherits: 2.0.4
-      readable-stream: 2.3.8
-      typedarray: 0.0.6
-    dev: true
-
-  /connect@3.7.0:
-    resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==}
-    engines: {node: '>= 0.10.0'}
-    dependencies:
-      debug: 2.6.9
-      finalhandler: 1.1.2
-      parseurl: 1.3.3
-      utils-merge: 1.0.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /console-browserify@1.2.0:
-    resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==}
-    dev: true
-
-  /constantinople@4.0.1:
-    resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==}
-    dependencies:
-      '@babel/parser': 7.23.5
-      '@babel/types': 7.23.5
-    dev: true
-
-  /constants-browserify@1.0.0:
-    resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==}
-    dev: true
-
-  /content-type@1.0.5:
-    resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
-    engines: {node: '>= 0.6'}
-    dev: true
-
-  /conventional-changelog-angular@5.0.13:
-    resolution: {integrity: sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==}
-    engines: {node: '>=10'}
-    dependencies:
-      compare-func: 2.0.0
-      q: 1.5.1
-    dev: true
-
-  /conventional-changelog-atom@2.0.8:
-    resolution: {integrity: sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==}
-    engines: {node: '>=10'}
-    dependencies:
-      q: 1.5.1
-    dev: true
-
-  /conventional-changelog-cli@2.2.2:
-    resolution: {integrity: sha512-8grMV5Jo8S0kP3yoMeJxV2P5R6VJOqK72IiSV9t/4H5r/HiRqEBQ83bYGuz4Yzfdj4bjaAEhZN/FFbsFXr5bOA==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      add-stream: 1.0.0
-      conventional-changelog: 3.1.25
-      lodash: 4.17.21
-      meow: 8.1.2
-      tempfile: 3.0.0
-    dev: true
-
-  /conventional-changelog-codemirror@2.0.8:
-    resolution: {integrity: sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==}
-    engines: {node: '>=10'}
-    dependencies:
-      q: 1.5.1
-    dev: true
-
-  /conventional-changelog-conventionalcommits@4.6.3:
-    resolution: {integrity: sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==}
-    engines: {node: '>=10'}
-    dependencies:
-      compare-func: 2.0.0
-      lodash: 4.17.21
-      q: 1.5.1
-    dev: true
-
-  /conventional-changelog-core@4.2.4:
-    resolution: {integrity: sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==}
-    engines: {node: '>=10'}
-    dependencies:
-      add-stream: 1.0.0
-      conventional-changelog-writer: 5.0.1
-      conventional-commits-parser: 3.2.4
-      dateformat: 3.0.3
-      get-pkg-repo: 4.2.1
-      git-raw-commits: 2.0.11
-      git-remote-origin-url: 2.0.0
-      git-semver-tags: 4.1.1
-      lodash: 4.17.21
-      normalize-package-data: 3.0.3
-      q: 1.5.1
-      read-pkg: 3.0.0
-      read-pkg-up: 3.0.0
-      through2: 4.0.2
-    dev: true
-
-  /conventional-changelog-ember@2.0.9:
-    resolution: {integrity: sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==}
-    engines: {node: '>=10'}
-    dependencies:
-      q: 1.5.1
-    dev: true
-
-  /conventional-changelog-eslint@3.0.9:
-    resolution: {integrity: sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==}
-    engines: {node: '>=10'}
-    dependencies:
-      q: 1.5.1
-    dev: true
-
-  /conventional-changelog-express@2.0.6:
-    resolution: {integrity: sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      q: 1.5.1
-    dev: true
-
-  /conventional-changelog-jquery@3.0.11:
-    resolution: {integrity: sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==}
-    engines: {node: '>=10'}
-    dependencies:
-      q: 1.5.1
-    dev: true
-
-  /conventional-changelog-jshint@2.0.9:
-    resolution: {integrity: sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==}
-    engines: {node: '>=10'}
-    dependencies:
-      compare-func: 2.0.0
-      q: 1.5.1
-    dev: true
-
-  /conventional-changelog-preset-loader@2.3.4:
-    resolution: {integrity: sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /conventional-changelog-writer@5.0.1:
-    resolution: {integrity: sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      conventional-commits-filter: 2.0.7
-      dateformat: 3.0.3
-      handlebars: 4.7.8
-      json-stringify-safe: 5.0.1
-      lodash: 4.17.21
-      meow: 8.1.2
-      semver: 6.3.1
-      split: 1.0.1
-      through2: 4.0.2
-    dev: true
-
-  /conventional-changelog@3.1.25:
-    resolution: {integrity: sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      conventional-changelog-angular: 5.0.13
-      conventional-changelog-atom: 2.0.8
-      conventional-changelog-codemirror: 2.0.8
-      conventional-changelog-conventionalcommits: 4.6.3
-      conventional-changelog-core: 4.2.4
-      conventional-changelog-ember: 2.0.9
-      conventional-changelog-eslint: 3.0.9
-      conventional-changelog-express: 2.0.6
-      conventional-changelog-jquery: 3.0.11
-      conventional-changelog-jshint: 2.0.9
-      conventional-changelog-preset-loader: 2.3.4
-    dev: true
-
-  /conventional-commits-filter@2.0.7:
-    resolution: {integrity: sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==}
-    engines: {node: '>=10'}
-    dependencies:
-      lodash.ismatch: 4.4.0
-      modify-values: 1.0.1
-    dev: true
-
-  /conventional-commits-parser@3.2.4:
-    resolution: {integrity: sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      JSONStream: 1.3.5
-      is-text-path: 1.0.1
-      lodash: 4.17.21
-      meow: 8.1.2
-      split2: 3.2.2
-      through2: 4.0.2
-    dev: true
-
-  /cookie@0.4.2:
-    resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
-    engines: {node: '>= 0.6'}
-    dev: true
-
-  /copy-anything@2.0.6:
-    resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
-    dependencies:
-      is-what: 3.14.1
-    dev: true
-
-  /copy-concurrently@1.0.5:
-    resolution: {integrity: sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==}
-    dependencies:
-      aproba: 1.2.0
-      fs-write-stream-atomic: 1.0.10
-      iferr: 0.1.5
-      mkdirp: 0.5.6
-      rimraf: 2.7.1
-      run-queue: 1.0.3
-    dev: true
-
-  /copy-descriptor@0.1.1:
-    resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /core-util-is@1.0.3:
-    resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
-    dev: true
-
-  /cors@2.8.5:
-    resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
-    engines: {node: '>= 0.10'}
-    dependencies:
-      object-assign: 4.1.1
-      vary: 1.1.2
-    dev: true
-
-  /create-ecdh@4.0.4:
-    resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==}
-    dependencies:
-      bn.js: 4.12.0
-      elliptic: 6.5.4
-    dev: true
-
-  /create-hash@1.2.0:
-    resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
-    dependencies:
-      cipher-base: 1.0.4
-      inherits: 2.0.4
-      md5.js: 1.3.5
-      ripemd160: 2.0.2
-      sha.js: 2.4.11
-    dev: true
-
-  /create-hmac@1.1.7:
-    resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==}
-    dependencies:
-      cipher-base: 1.0.4
-      create-hash: 1.2.0
-      inherits: 2.0.4
-      ripemd160: 2.0.2
-      safe-buffer: 5.2.1
-      sha.js: 2.4.11
-    dev: true
-
-  /create-require@1.1.1:
-    resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
-    dev: true
-
-  /cross-fetch@3.1.5:
-    resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==}
-    dependencies:
-      node-fetch: 2.6.7
-    transitivePeerDependencies:
-      - encoding
-    dev: true
-
-  /cross-spawn@5.1.0:
-    resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
-    dependencies:
-      lru-cache: 4.1.5
-      shebang-command: 1.2.0
-      which: 1.3.1
-    dev: true
-
-  /cross-spawn@7.0.3:
-    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
-    engines: {node: '>= 8'}
-    dependencies:
-      path-key: 3.1.1
-      shebang-command: 2.0.0
-      which: 2.0.2
-    dev: true
-
-  /crypto-browserify@3.12.0:
-    resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==}
-    dependencies:
-      browserify-cipher: 1.0.1
-      browserify-sign: 4.2.2
-      create-ecdh: 4.0.4
-      create-hash: 1.2.0
-      create-hmac: 1.1.7
-      diffie-hellman: 5.0.3
-      inherits: 2.0.4
-      pbkdf2: 3.1.2
-      public-encrypt: 4.0.3
-      randombytes: 2.1.0
-      randomfill: 1.0.4
-    dev: true
-
-  /css@3.0.0:
-    resolution: {integrity: sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==}
-    dependencies:
-      inherits: 2.0.4
-      source-map: 0.6.1
-      source-map-resolve: 0.6.0
-    dev: true
-
-  /cssesc@3.0.0:
-    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dev: true
-
-  /cssom@0.3.8:
-    resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
-    dev: true
-
-  /cssom@0.5.0:
-    resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==}
-    dev: true
-
-  /cssstyle@2.3.0:
-    resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==}
-    engines: {node: '>=8'}
-    dependencies:
-      cssom: 0.3.8
-    dev: true
-
-  /csstype@3.1.2:
-    resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
-
-  /custom-event@1.0.1:
-    resolution: {integrity: sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==}
-    dev: true
-
-  /cyclist@1.0.2:
-    resolution: {integrity: sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA==}
-    dev: true
-
-  /dargs@7.0.0:
-    resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /data-urls@3.0.2:
-    resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==}
-    engines: {node: '>=12'}
-    dependencies:
-      abab: 2.0.6
-      whatwg-mimetype: 3.0.0
-      whatwg-url: 11.0.0
-    dev: true
-
-  /date-format@4.0.14:
-    resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==}
-    engines: {node: '>=4.0'}
-    dev: true
-
-  /dateformat@3.0.3:
-    resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==}
-    dev: true
-
-  /de-indent@1.0.2:
-    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
-
-  /debug@2.6.9:
-    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.0.0
-    dev: true
-
-  /debug@3.2.7:
-    resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
-    requiresBuild: true
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.1.3
-    dev: true
-    optional: true
-
-  /debug@4.3.4(supports-color@9.4.0):
-    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
-    engines: {node: '>=6.0'}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.1.2
-      supports-color: 9.4.0
-    dev: true
-
-  /decamelize-keys@1.1.1:
-    resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      decamelize: 1.2.0
-      map-obj: 1.0.1
-    dev: true
-
-  /decamelize@1.2.0:
-    resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /decimal.js@10.4.3:
-    resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
-    dev: true
-
-  /decode-uri-component@0.2.2:
-    resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
-    engines: {node: '>=0.10'}
-    dev: true
-
-  /deep-eql@4.1.3:
-    resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
-    engines: {node: '>=6'}
-    dependencies:
-      type-detect: 4.0.8
-    dev: true
-
-  /deepmerge@4.3.1:
-    resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /define-data-property@1.1.1:
-    resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      get-intrinsic: 1.2.2
-      gopd: 1.0.1
-      has-property-descriptors: 1.0.1
-    dev: true
-
-  /define-properties@1.2.1:
-    resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      define-data-property: 1.1.1
-      has-property-descriptors: 1.0.1
-      object-keys: 1.1.1
-    dev: true
-
-  /define-property@0.2.5:
-    resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-descriptor: 0.1.7
-    dev: true
-
-  /define-property@1.0.0:
-    resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-descriptor: 1.0.3
-    dev: true
-
-  /define-property@2.0.2:
-    resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-descriptor: 1.0.3
-      isobject: 3.0.1
-    dev: true
-
-  /delayed-stream@1.0.0:
-    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
-    engines: {node: '>=0.4.0'}
-    dev: true
-
-  /depd@2.0.0:
-    resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
-    engines: {node: '>= 0.8'}
-    dev: true
-
-  /des.js@1.1.0:
-    resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==}
-    dependencies:
-      inherits: 2.0.4
-      minimalistic-assert: 1.0.1
-    dev: true
-
-  /destroy@1.2.0:
-    resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
-    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
-    dev: true
-
-  /devtools-protocol@0.0.1001819:
-    resolution: {integrity: sha512-G6OsIFnv/rDyxSqBa2lDLR6thp9oJioLsb2Gl+LbQlyoA9/OBAkrTU9jiCcQ8Pnh7z4d6slDiLaogR5hzgJLmQ==}
-    dev: true
-
-  /di@0.0.1:
-    resolution: {integrity: sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==}
-    dev: true
-
-  /diff-sequences@29.6.3:
-    resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    dev: true
-
-  /diff@4.0.2:
-    resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
-    engines: {node: '>=0.3.1'}
-    dev: true
-
-  /diffie-hellman@5.0.3:
-    resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
-    dependencies:
-      bn.js: 4.12.0
-      miller-rabin: 4.0.1
-      randombytes: 2.1.0
-    dev: true
-
-  /doctypes@1.1.0:
-    resolution: {integrity: sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==}
-    dev: true
-
-  /dom-serialize@2.2.1:
-    resolution: {integrity: sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==}
-    dependencies:
-      custom-event: 1.0.1
-      ent: 2.2.0
-      extend: 3.0.2
-      void-elements: 2.0.1
-    dev: true
-
-  /domain-browser@1.2.0:
-    resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==}
-    engines: {node: '>=0.4', npm: '>=1.2'}
-    dev: true
-
-  /domexception@4.0.0:
-    resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==}
-    engines: {node: '>=12'}
-    deprecated: Use your platform's native DOMException instead
-    dependencies:
-      webidl-conversions: 7.0.0
-    dev: true
-
-  /dot-prop@5.3.0:
-    resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
-    engines: {node: '>=8'}
-    dependencies:
-      is-obj: 2.0.0
-    dev: true
-
-  /duplexify@3.7.1:
-    resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==}
-    dependencies:
-      end-of-stream: 1.4.4
-      inherits: 2.0.4
-      readable-stream: 2.3.8
-      stream-shift: 1.0.1
-    dev: true
-
-  /eastasianwidth@0.2.0:
-    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
-    dev: true
-
-  /ee-first@1.1.1:
-    resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
-    dev: true
-
-  /elliptic@6.5.4:
-    resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==}
-    dependencies:
-      bn.js: 4.12.0
-      brorand: 1.1.0
-      hash.js: 1.1.7
-      hmac-drbg: 1.0.1
-      inherits: 2.0.4
-      minimalistic-assert: 1.0.1
-      minimalistic-crypto-utils: 1.0.1
-    dev: true
-
-  /emoji-regex@8.0.0:
-    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
-    dev: true
-
-  /emoji-regex@9.2.2:
-    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
-    dev: true
-
-  /emojis-list@3.0.0:
-    resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
-    engines: {node: '>= 4'}
-    dev: true
-
-  /encodeurl@1.0.2:
-    resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
-    engines: {node: '>= 0.8'}
-    dev: true
-
-  /end-of-stream@1.4.4:
-    resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
-    dependencies:
-      once: 1.4.0
-    dev: true
-
-  /engine.io-parser@5.2.1:
-    resolution: {integrity: sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==}
-    engines: {node: '>=10.0.0'}
-    dev: true
-
-  /engine.io@6.5.4:
-    resolution: {integrity: sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==}
-    engines: {node: '>=10.2.0'}
-    dependencies:
-      '@types/cookie': 0.4.1
-      '@types/cors': 2.8.17
-      '@types/node': 20.10.3
-      accepts: 1.3.8
-      base64id: 2.0.0
-      cookie: 0.4.2
-      cors: 2.8.5
-      debug: 4.3.4(supports-color@9.4.0)
-      engine.io-parser: 5.2.1
-      ws: 8.11.0
-    transitivePeerDependencies:
-      - bufferutil
-      - supports-color
-      - utf-8-validate
-    dev: true
-
-  /enhanced-resolve@4.5.0:
-    resolution: {integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      graceful-fs: 4.2.11
-      memory-fs: 0.5.0
-      tapable: 1.1.3
-    dev: true
-
-  /enquirer@2.4.1:
-    resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==}
-    engines: {node: '>=8.6'}
-    dependencies:
-      ansi-colors: 4.1.3
-      strip-ansi: 6.0.1
-    dev: true
-
-  /ent@2.2.0:
-    resolution: {integrity: sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==}
-    dev: true
-
-  /errno@0.1.8:
-    resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
-    hasBin: true
-    dependencies:
-      prr: 1.0.1
-    dev: true
-
-  /error-ex@1.3.2:
-    resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
-    dependencies:
-      is-arrayish: 0.2.1
-    dev: true
-
-  /esbuild@0.19.8:
-    resolution: {integrity: sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==}
-    engines: {node: '>=12'}
-    hasBin: true
-    requiresBuild: true
-    optionalDependencies:
-      '@esbuild/android-arm': 0.19.8
-      '@esbuild/android-arm64': 0.19.8
-      '@esbuild/android-x64': 0.19.8
-      '@esbuild/darwin-arm64': 0.19.8
-      '@esbuild/darwin-x64': 0.19.8
-      '@esbuild/freebsd-arm64': 0.19.8
-      '@esbuild/freebsd-x64': 0.19.8
-      '@esbuild/linux-arm': 0.19.8
-      '@esbuild/linux-arm64': 0.19.8
-      '@esbuild/linux-ia32': 0.19.8
-      '@esbuild/linux-loong64': 0.19.8
-      '@esbuild/linux-mips64el': 0.19.8
-      '@esbuild/linux-ppc64': 0.19.8
-      '@esbuild/linux-riscv64': 0.19.8
-      '@esbuild/linux-s390x': 0.19.8
-      '@esbuild/linux-x64': 0.19.8
-      '@esbuild/netbsd-x64': 0.19.8
-      '@esbuild/openbsd-x64': 0.19.8
-      '@esbuild/sunos-x64': 0.19.8
-      '@esbuild/win32-arm64': 0.19.8
-      '@esbuild/win32-ia32': 0.19.8
-      '@esbuild/win32-x64': 0.19.8
-    dev: true
-
-  /escalade@3.1.1:
-    resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /escape-html@1.0.3:
-    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
-    dev: true
-
-  /escape-string-regexp@1.0.5:
-    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
-    engines: {node: '>=0.8.0'}
-    dev: true
-
-  /escodegen@2.1.0:
-    resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==}
-    engines: {node: '>=6.0'}
-    hasBin: true
-    dependencies:
-      esprima: 4.0.1
-      estraverse: 5.3.0
-      esutils: 2.0.3
-    optionalDependencies:
-      source-map: 0.6.1
-    dev: true
-
-  /eslint-scope@4.0.3:
-    resolution: {integrity: sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==}
-    engines: {node: '>=4.0.0'}
-    dependencies:
-      esrecurse: 4.3.0
-      estraverse: 4.3.0
-    dev: true
-
-  /esprima@4.0.1:
-    resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dev: true
-
-  /esrecurse@4.3.0:
-    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
-    engines: {node: '>=4.0'}
-    dependencies:
-      estraverse: 5.3.0
-    dev: true
-
-  /estraverse@4.3.0:
-    resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
-    engines: {node: '>=4.0'}
-    dev: true
-
-  /estraverse@5.3.0:
-    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
-    engines: {node: '>=4.0'}
-    dev: true
-
-  /estree-walker@1.0.1:
-    resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
-    dev: true
-
-  /estree-walker@2.0.2:
-    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
-    dev: true
-
-  /esutils@2.0.3:
-    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /eventemitter3@4.0.7:
-    resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
-    dev: true
-
-  /events@3.3.0:
-    resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
-    engines: {node: '>=0.8.x'}
-    dev: true
-
-  /evp_bytestokey@1.0.3:
-    resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==}
-    dependencies:
-      md5.js: 1.3.5
-      safe-buffer: 5.2.1
-    dev: true
-
-  /execa@0.8.0:
-    resolution: {integrity: sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==}
-    engines: {node: '>=4'}
-    dependencies:
-      cross-spawn: 5.1.0
-      get-stream: 3.0.0
-      is-stream: 1.1.0
-      npm-run-path: 2.0.2
-      p-finally: 1.0.0
-      signal-exit: 3.0.7
-      strip-eof: 1.0.0
-    dev: true
-
-  /execa@4.1.0:
-    resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
-    engines: {node: '>=10'}
-    dependencies:
-      cross-spawn: 7.0.3
-      get-stream: 5.2.0
-      human-signals: 1.1.1
-      is-stream: 2.0.1
-      merge-stream: 2.0.0
-      npm-run-path: 4.0.1
-      onetime: 5.1.2
-      signal-exit: 3.0.7
-      strip-final-newline: 2.0.0
-    dev: true
-
-  /execa@5.1.1:
-    resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
-    engines: {node: '>=10'}
-    dependencies:
-      cross-spawn: 7.0.3
-      get-stream: 6.0.1
-      human-signals: 2.1.0
-      is-stream: 2.0.1
-      merge-stream: 2.0.0
-      npm-run-path: 4.0.1
-      onetime: 5.1.2
-      signal-exit: 3.0.7
-      strip-final-newline: 2.0.0
-    dev: true
-
-  /execa@8.0.1:
-    resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
-    engines: {node: '>=16.17'}
-    dependencies:
-      cross-spawn: 7.0.3
-      get-stream: 8.0.1
-      human-signals: 5.0.0
-      is-stream: 3.0.0
-      merge-stream: 2.0.0
-      npm-run-path: 5.1.0
-      onetime: 6.0.0
-      signal-exit: 4.1.0
-      strip-final-newline: 3.0.0
-    dev: true
-
-  /expand-brackets@2.1.4:
-    resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      debug: 2.6.9
-      define-property: 0.2.5
-      extend-shallow: 2.0.1
-      posix-character-classes: 0.1.1
-      regex-not: 1.0.2
-      snapdragon: 0.8.2
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /extend-shallow@2.0.1:
-    resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-extendable: 0.1.1
-    dev: true
-
-  /extend-shallow@3.0.2:
-    resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      assign-symbols: 1.0.0
-      is-extendable: 1.0.1
-    dev: true
-
-  /extend@3.0.2:
-    resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
-    dev: true
-
-  /extglob@2.0.4:
-    resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      array-unique: 0.3.2
-      define-property: 1.0.0
-      expand-brackets: 2.1.4
-      extend-shallow: 2.0.1
-      fragment-cache: 0.2.1
-      regex-not: 1.0.2
-      snapdragon: 0.8.2
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /extract-zip@2.0.1:
-    resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==}
-    engines: {node: '>= 10.17.0'}
-    hasBin: true
-    dependencies:
-      debug: 4.3.4(supports-color@9.4.0)
-      get-stream: 5.2.0
-      yauzl: 2.10.0
-    optionalDependencies:
-      '@types/yauzl': 2.10.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /fast-deep-equal@3.1.3:
-    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
-    dev: true
-
-  /fast-json-stable-stringify@2.1.0:
-    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
-    dev: true
-
-  /fd-slicer@1.1.0:
-    resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
-    dependencies:
-      pend: 1.2.0
-    dev: true
-
-  /figgy-pudding@3.5.2:
-    resolution: {integrity: sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==}
-    dev: true
-
-  /file-loader@3.0.1(webpack@4.47.0):
-    resolution: {integrity: sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==}
-    engines: {node: '>= 6.9.0'}
-    peerDependencies:
-      webpack: ^4.0.0
-    dependencies:
-      loader-utils: 1.4.2
-      schema-utils: 1.0.0
-      webpack: 4.47.0
-    dev: true
-
-  /file-uri-to-path@1.0.0:
-    resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /fill-range@4.0.0:
-    resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      extend-shallow: 2.0.1
-      is-number: 3.0.0
-      repeat-string: 1.6.1
-      to-regex-range: 2.1.1
-    dev: true
-
-  /fill-range@7.0.1:
-    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      to-regex-range: 5.0.1
-    dev: true
-
-  /finalhandler@1.1.2:
-    resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      debug: 2.6.9
-      encodeurl: 1.0.2
-      escape-html: 1.0.3
-      on-finished: 2.3.0
-      parseurl: 1.3.3
-      statuses: 1.5.0
-      unpipe: 1.0.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /find-cache-dir@2.1.0:
-    resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==}
-    engines: {node: '>=6'}
-    dependencies:
-      commondir: 1.0.1
-      make-dir: 2.1.0
-      pkg-dir: 3.0.0
-    dev: true
-
-  /find-cache-dir@3.3.2:
-    resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
-    engines: {node: '>=8'}
-    dependencies:
-      commondir: 1.0.1
-      make-dir: 3.1.0
-      pkg-dir: 4.2.0
-    dev: true
-
-  /find-up@2.1.0:
-    resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==}
-    engines: {node: '>=4'}
-    dependencies:
-      locate-path: 2.0.0
-    dev: true
-
-  /find-up@3.0.0:
-    resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
-    engines: {node: '>=6'}
-    dependencies:
-      locate-path: 3.0.0
-    dev: true
-
-  /find-up@4.1.0:
-    resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
-    engines: {node: '>=8'}
-    dependencies:
-      locate-path: 5.0.0
-      path-exists: 4.0.0
-    dev: true
-
-  /flatted@3.2.9:
-    resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
-    dev: true
-
-  /flush-write-stream@1.1.1:
-    resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==}
-    dependencies:
-      inherits: 2.0.4
-      readable-stream: 2.3.8
-    dev: true
-
-  /follow-redirects@1.15.3:
-    resolution: {integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==}
-    engines: {node: '>=4.0'}
-    peerDependencies:
-      debug: '*'
-    peerDependenciesMeta:
-      debug:
-        optional: true
-    dev: true
-
-  /for-in@1.0.2:
-    resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /form-data@4.0.0:
-    resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
-    engines: {node: '>= 6'}
-    dependencies:
-      asynckit: 0.4.0
-      combined-stream: 1.0.8
-      mime-types: 2.1.35
-    dev: true
-
-  /fragment-cache@0.2.1:
-    resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      map-cache: 0.2.2
-    dev: true
-
-  /from2@2.3.0:
-    resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==}
-    dependencies:
-      inherits: 2.0.4
-      readable-stream: 2.3.8
-    dev: true
-
-  /fs-constants@1.0.0:
-    resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
-    dev: true
-
-  /fs-extra@10.1.0:
-    resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
-    engines: {node: '>=12'}
-    dependencies:
-      graceful-fs: 4.2.11
-      jsonfile: 6.1.0
-      universalify: 2.0.1
-    dev: true
-
-  /fs-extra@7.0.1:
-    resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
-    engines: {node: '>=6 <7 || >=8'}
-    dependencies:
-      graceful-fs: 4.2.11
-      jsonfile: 4.0.0
-      universalify: 0.1.2
-    dev: true
-
-  /fs-extra@8.1.0:
-    resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
-    engines: {node: '>=6 <7 || >=8'}
-    dependencies:
-      graceful-fs: 4.2.11
-      jsonfile: 4.0.0
-      universalify: 0.1.2
-    dev: true
-
-  /fs-write-stream-atomic@1.0.10:
-    resolution: {integrity: sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==}
-    dependencies:
-      graceful-fs: 4.2.11
-      iferr: 0.1.5
-      imurmurhash: 0.1.4
-      readable-stream: 2.3.8
-    dev: true
-
-  /fs.realpath@1.0.0:
-    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
-    dev: true
-
-  /fsevents@1.2.13:
-    resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==}
-    engines: {node: '>= 4.0'}
-    os: [darwin]
-    deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2
-    requiresBuild: true
-    dependencies:
-      bindings: 1.5.0
-      nan: 2.18.0
-    dev: true
-    optional: true
-
-  /fsevents@2.3.3:
-    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
-    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /function-bind@1.1.2:
-    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
-
-  /generic-names@4.0.0:
-    resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==}
-    dependencies:
-      loader-utils: 3.2.1
-    dev: true
-
-  /get-caller-file@2.0.5:
-    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
-    engines: {node: 6.* || 8.* || >= 10.*}
-    dev: true
-
-  /get-func-name@2.0.2:
-    resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
-    dev: true
-
-  /get-intrinsic@1.2.2:
-    resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
-    dependencies:
-      function-bind: 1.1.2
-      has-proto: 1.0.1
-      has-symbols: 1.0.3
-      hasown: 2.0.0
-    dev: true
-
-  /get-pkg-repo@4.2.1:
-    resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==}
-    engines: {node: '>=6.9.0'}
-    hasBin: true
-    dependencies:
-      '@hutson/parse-repository-url': 3.0.2
-      hosted-git-info: 4.1.0
-      through2: 2.0.5
-      yargs: 16.2.0
-    dev: true
-
-  /get-stream@3.0.0:
-    resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /get-stream@5.2.0:
-    resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
-    engines: {node: '>=8'}
-    dependencies:
-      pump: 3.0.0
-    dev: true
-
-  /get-stream@6.0.1:
-    resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /get-stream@8.0.1:
-    resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
-    engines: {node: '>=16'}
-    dev: true
-
-  /get-value@2.0.6:
-    resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /git-raw-commits@2.0.11:
-    resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      dargs: 7.0.0
-      lodash: 4.17.21
-      meow: 8.1.2
-      split2: 3.2.2
-      through2: 4.0.2
-    dev: true
-
-  /git-remote-origin-url@2.0.0:
-    resolution: {integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==}
-    engines: {node: '>=4'}
-    dependencies:
-      gitconfiglocal: 1.0.0
-      pify: 2.3.0
-    dev: true
-
-  /git-semver-tags@4.1.1:
-    resolution: {integrity: sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      meow: 8.1.2
-      semver: 6.3.1
-    dev: true
-
-  /gitconfiglocal@1.0.0:
-    resolution: {integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==}
-    dependencies:
-      ini: 1.3.8
-    dev: true
-
-  /glob-parent@3.1.0:
-    resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==}
-    requiresBuild: true
-    dependencies:
-      is-glob: 3.1.0
-      path-dirname: 1.0.2
-    dev: true
-    optional: true
-
-  /glob-parent@5.1.2:
-    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
-    engines: {node: '>= 6'}
-    dependencies:
-      is-glob: 4.0.3
-    dev: true
-
-  /glob@7.2.3:
-    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
-    dependencies:
-      fs.realpath: 1.0.0
-      inflight: 1.0.6
-      inherits: 2.0.4
-      minimatch: 3.1.2
-      once: 1.4.0
-      path-is-absolute: 1.0.1
-    dev: true
-
-  /gopd@1.0.1:
-    resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
-    dependencies:
-      get-intrinsic: 1.2.2
-    dev: true
-
-  /graceful-fs@4.2.11:
-    resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
-    dev: true
-
-  /handlebars@4.7.8:
-    resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
-    engines: {node: '>=0.4.7'}
-    hasBin: true
-    dependencies:
-      minimist: 1.2.8
-      neo-async: 2.6.2
-      source-map: 0.6.1
-      wordwrap: 1.0.0
-    optionalDependencies:
-      uglify-js: 3.17.4
-    dev: true
-
-  /hard-rejection@2.1.0:
-    resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /has-flag@3.0.0:
-    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /has-flag@4.0.0:
-    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
-    engines: {node: '>=8'}
-
-  /has-property-descriptors@1.0.1:
-    resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==}
-    dependencies:
-      get-intrinsic: 1.2.2
-    dev: true
-
-  /has-proto@1.0.1:
-    resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /has-symbols@1.0.3:
-    resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /has-tostringtag@1.0.0:
-    resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      has-symbols: 1.0.3
-    dev: true
-
-  /has-value@0.3.1:
-    resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      get-value: 2.0.6
-      has-values: 0.1.4
-      isobject: 2.1.0
-    dev: true
-
-  /has-value@1.0.0:
-    resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      get-value: 2.0.6
-      has-values: 1.0.0
-      isobject: 3.0.1
-    dev: true
-
-  /has-values@0.1.4:
-    resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /has-values@1.0.0:
-    resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-number: 3.0.0
-      kind-of: 4.0.0
-    dev: true
-
-  /hash-base@3.1.0:
-    resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==}
-    engines: {node: '>=4'}
-    dependencies:
-      inherits: 2.0.4
-      readable-stream: 3.6.2
-      safe-buffer: 5.2.1
-    dev: true
-
-  /hash-sum@2.0.0:
-    resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
-
-  /hash.js@1.1.7:
-    resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==}
-    dependencies:
-      inherits: 2.0.4
-      minimalistic-assert: 1.0.1
-    dev: true
-
-  /hasown@2.0.0:
-    resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      function-bind: 1.1.2
-
-  /he@1.2.0:
-    resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
-    hasBin: true
-
-  /hmac-drbg@1.0.1:
-    resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==}
-    dependencies:
-      hash.js: 1.1.7
-      minimalistic-assert: 1.0.1
-      minimalistic-crypto-utils: 1.0.1
-    dev: true
-
-  /hosted-git-info@2.8.9:
-    resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
-    dev: true
-
-  /hosted-git-info@4.1.0:
-    resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==}
-    engines: {node: '>=10'}
-    dependencies:
-      lru-cache: 6.0.0
-    dev: true
-
-  /html-encoding-sniffer@3.0.0:
-    resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
-    engines: {node: '>=12'}
-    dependencies:
-      whatwg-encoding: 2.0.0
-    dev: true
-
-  /http-errors@2.0.0:
-    resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      depd: 2.0.0
-      inherits: 2.0.4
-      setprototypeof: 1.2.0
-      statuses: 2.0.1
-      toidentifier: 1.0.1
-    dev: true
-
-  /http-proxy-agent@5.0.0:
-    resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
-    engines: {node: '>= 6'}
-    dependencies:
-      '@tootallnate/once': 2.0.0
-      agent-base: 6.0.2
-      debug: 4.3.4(supports-color@9.4.0)
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /http-proxy@1.18.1:
-    resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==}
-    engines: {node: '>=8.0.0'}
-    dependencies:
-      eventemitter3: 4.0.7
-      follow-redirects: 1.15.3
-      requires-port: 1.0.0
-    transitivePeerDependencies:
-      - debug
-    dev: true
-
-  /https-browserify@1.0.0:
-    resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==}
-    dev: true
-
-  /https-proxy-agent@5.0.1:
-    resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
-    engines: {node: '>= 6'}
-    dependencies:
-      agent-base: 6.0.2
-      debug: 4.3.4(supports-color@9.4.0)
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /human-signals@1.1.1:
-    resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
-    engines: {node: '>=8.12.0'}
-    dev: true
-
-  /human-signals@2.1.0:
-    resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
-    engines: {node: '>=10.17.0'}
-    dev: true
-
-  /human-signals@5.0.0:
-    resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
-    engines: {node: '>=16.17.0'}
-    dev: true
-
-  /iconv-lite@0.4.24:
-    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      safer-buffer: 2.1.2
-    dev: true
-
-  /iconv-lite@0.6.3:
-    resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      safer-buffer: 2.1.2
-    dev: true
-
-  /icss-replace-symbols@1.1.0:
-    resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==}
-    dev: true
-
-  /icss-utils@5.1.0(postcss@8.4.32):
-    resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
-    engines: {node: ^10 || ^12 || >= 14}
-    peerDependencies:
-      postcss: ^8.1.0
-    dependencies:
-      postcss: 8.4.32
-    dev: true
-
-  /ieee754@1.2.1:
-    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
-    dev: true
-
-  /iferr@0.1.5:
-    resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==}
-    dev: true
-
-  /image-size@0.5.5:
-    resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==}
-    engines: {node: '>=0.10.0'}
-    hasBin: true
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /immutable@4.3.4:
-    resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==}
-    dev: true
-
-  /import-lazy@4.0.0:
-    resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /imurmurhash@0.1.4:
-    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
-    engines: {node: '>=0.8.19'}
-    dev: true
-
-  /indent-string@4.0.0:
-    resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /infer-owner@1.0.4:
-    resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==}
-    dev: true
-
-  /inflight@1.0.6:
-    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
-    dependencies:
-      once: 1.4.0
-      wrappy: 1.0.2
-    dev: true
-
-  /inherits@2.0.3:
-    resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==}
-    dev: true
-
-  /inherits@2.0.4:
-    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
-    dev: true
-
-  /ini@1.3.8:
-    resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
-    dev: true
-
-  /interpret@1.4.0:
-    resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
-    engines: {node: '>= 0.10'}
-    dev: true
-
-  /is-accessor-descriptor@1.0.1:
-    resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==}
-    engines: {node: '>= 0.10'}
-    dependencies:
-      hasown: 2.0.0
-    dev: true
-
-  /is-arrayish@0.2.1:
-    resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
-    dev: true
-
-  /is-binary-path@1.0.1:
-    resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==}
-    engines: {node: '>=0.10.0'}
-    requiresBuild: true
-    dependencies:
-      binary-extensions: 1.13.1
-    dev: true
-    optional: true
-
-  /is-binary-path@2.1.0:
-    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
-    engines: {node: '>=8'}
-    dependencies:
-      binary-extensions: 2.2.0
-    dev: true
-
-  /is-buffer@1.1.6:
-    resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
-    dev: true
-
-  /is-builtin-module@3.2.1:
-    resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==}
-    engines: {node: '>=6'}
-    dependencies:
-      builtin-modules: 3.3.0
-    dev: true
-
-  /is-ci@1.2.1:
-    resolution: {integrity: sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==}
-    hasBin: true
-    dependencies:
-      ci-info: 1.6.0
-    dev: true
-
-  /is-core-module@2.13.1:
-    resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
-    dependencies:
-      hasown: 2.0.0
-
-  /is-data-descriptor@1.0.1:
-    resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      hasown: 2.0.0
-    dev: true
-
-  /is-descriptor@0.1.7:
-    resolution: {integrity: sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      is-accessor-descriptor: 1.0.1
-      is-data-descriptor: 1.0.1
-    dev: true
-
-  /is-descriptor@1.0.3:
-    resolution: {integrity: sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      is-accessor-descriptor: 1.0.1
-      is-data-descriptor: 1.0.1
-    dev: true
-
-  /is-expression@4.0.0:
-    resolution: {integrity: sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==}
-    dependencies:
-      acorn: 7.4.1
-      object-assign: 4.1.1
-    dev: true
-
-  /is-extendable@0.1.1:
-    resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /is-extendable@1.0.1:
-    resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-plain-object: 2.0.4
-    dev: true
-
-  /is-extglob@2.1.1:
-    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /is-fullwidth-code-point@3.0.0:
-    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /is-fullwidth-code-point@4.0.0:
-    resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /is-glob@3.1.0:
-    resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==}
-    engines: {node: '>=0.10.0'}
-    requiresBuild: true
-    dependencies:
-      is-extglob: 2.1.1
-    dev: true
-    optional: true
-
-  /is-glob@4.0.3:
-    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-extglob: 2.1.1
-    dev: true
-
-  /is-module@1.0.0:
-    resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
-    dev: true
-
-  /is-number@3.0.0:
-    resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      kind-of: 3.2.2
-    dev: true
-
-  /is-number@7.0.0:
-    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
-    engines: {node: '>=0.12.0'}
-    dev: true
-
-  /is-obj@2.0.0:
-    resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /is-plain-obj@1.1.0:
-    resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /is-plain-object@2.0.4:
-    resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      isobject: 3.0.1
-    dev: true
-
-  /is-potential-custom-element-name@1.0.1:
-    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
-    dev: true
-
-  /is-promise@2.2.2:
-    resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
-    dev: true
-
-  /is-reference@1.2.1:
-    resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
-    dependencies:
-      '@types/estree': 0.0.48
-    dev: true
-
-  /is-regex@1.1.4:
-    resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.5
-      has-tostringtag: 1.0.0
-    dev: true
-
-  /is-stream@1.1.0:
-    resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /is-stream@2.0.1:
-    resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /is-stream@3.0.0:
-    resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-    dev: true
-
-  /is-text-path@1.0.1:
-    resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      text-extensions: 1.9.0
-    dev: true
-
-  /is-what@3.14.1:
-    resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==}
-    dev: true
-
-  /is-windows@1.0.2:
-    resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /is-wsl@1.1.0:
-    resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /isarray@1.0.0:
-    resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
-    dev: true
-
-  /isbinaryfile@4.0.10:
-    resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==}
-    engines: {node: '>= 8.0.0'}
-    dev: true
-
-  /isexe@2.0.0:
-    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
-    dev: true
-
-  /isobject@2.1.0:
-    resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      isarray: 1.0.0
-    dev: true
-
-  /isobject@3.0.1:
-    resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /jasmine-core@4.6.0:
-    resolution: {integrity: sha512-O236+gd0ZXS8YAjFx8xKaJ94/erqUliEkJTDedyE7iHvv4ZVqi+q+8acJxu05/WJDKm512EUNn809In37nWlAQ==}
-    dev: true
-
-  /jju@1.4.0:
-    resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==}
-    dev: true
-
-  /js-stringify@1.0.2:
-    resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==}
-    dev: true
-
-  /js-tokens@4.0.0:
-    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
-    dev: true
-
-  /jsdom@19.0.0:
-    resolution: {integrity: sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==}
-    engines: {node: '>=12'}
-    peerDependencies:
-      canvas: ^2.5.0
-    peerDependenciesMeta:
-      canvas:
-        optional: true
-    dependencies:
-      abab: 2.0.6
-      acorn: 8.11.2
-      acorn-globals: 6.0.0
-      cssom: 0.5.0
-      cssstyle: 2.3.0
-      data-urls: 3.0.2
-      decimal.js: 10.4.3
-      domexception: 4.0.0
-      escodegen: 2.1.0
-      form-data: 4.0.0
-      html-encoding-sniffer: 3.0.0
-      http-proxy-agent: 5.0.0
-      https-proxy-agent: 5.0.1
-      is-potential-custom-element-name: 1.0.1
-      nwsapi: 2.2.7
-      parse5: 6.0.1
-      saxes: 5.0.1
-      symbol-tree: 3.2.4
-      tough-cookie: 4.1.3
-      w3c-hr-time: 1.0.2
-      w3c-xmlserializer: 3.0.0
-      webidl-conversions: 7.0.0
-      whatwg-encoding: 2.0.0
-      whatwg-mimetype: 3.0.0
-      whatwg-url: 10.0.0
-      ws: 8.14.2
-      xml-name-validator: 4.0.0
-    transitivePeerDependencies:
-      - bufferutil
-      - supports-color
-      - utf-8-validate
-    dev: true
-
-  /json-parse-better-errors@1.0.2:
-    resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==}
-    dev: true
-
-  /json-parse-even-better-errors@2.3.1:
-    resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
-    dev: true
-
-  /json-schema-traverse@0.4.1:
-    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
-    dev: true
-
-  /json-stringify-safe@5.0.1:
-    resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
-    dev: true
-
-  /json5@1.0.2:
-    resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
-    hasBin: true
-    dependencies:
-      minimist: 1.2.8
-    dev: true
-
-  /jsonc-parser@3.2.0:
-    resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
-    dev: true
-
-  /jsonfile@4.0.0:
-    resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
-    optionalDependencies:
-      graceful-fs: 4.2.11
-    dev: true
-
-  /jsonfile@6.1.0:
-    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
-    dependencies:
-      universalify: 2.0.1
-    optionalDependencies:
-      graceful-fs: 4.2.11
-    dev: true
-
-  /jsonparse@1.3.1:
-    resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
-    engines: {'0': node >= 0.2.0}
-    dev: true
-
-  /jstransformer@1.0.0:
-    resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==}
-    dependencies:
-      is-promise: 2.2.2
-      promise: 7.3.1
-    dev: true
-
-  /karma-chrome-launcher@3.2.0:
-    resolution: {integrity: sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==}
-    dependencies:
-      which: 1.3.1
-    dev: true
-
-  /karma-cli@2.0.0:
-    resolution: {integrity: sha512-1Kb28UILg1ZsfqQmeELbPzuEb5C6GZJfVIk0qOr8LNYQuYWmAaqP16WpbpKEjhejDrDYyYOwwJXSZO6u7q5Pvw==}
-    engines: {node: '>= 6'}
-    hasBin: true
-    dependencies:
-      resolve: 1.22.8
-    dev: true
-
-  /karma-esbuild@2.3.0(esbuild@0.19.8):
-    resolution: {integrity: sha512-iW3DjSGohEEkufSDmXRPZP7CNP0ye+Xt8fBCcenLqPL2u8+VHZYwlzwYyfs60vjhdf1i04xekhzI7gu8as1CLg==}
-    peerDependencies:
-      esbuild: '>=0.17.0'
-    dependencies:
-      chokidar: 3.5.3
-      esbuild: 0.19.8
-      source-map: 0.6.1
-    dev: true
-
-  /karma-jasmine@5.1.0(karma@6.4.2):
-    resolution: {integrity: sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==}
-    engines: {node: '>=12'}
-    peerDependencies:
-      karma: ^6.0.0
-    dependencies:
-      jasmine-core: 4.6.0
-      karma: 6.4.2
-    dev: true
-
-  /karma@6.4.2:
-    resolution: {integrity: sha512-C6SU/53LB31BEgRg+omznBEMY4SjHU3ricV6zBcAe1EeILKkeScr+fZXtaI5WyDbkVowJxxAI6h73NcFPmXolQ==}
-    engines: {node: '>= 10'}
-    hasBin: true
-    dependencies:
-      '@colors/colors': 1.5.0
-      body-parser: 1.20.2
-      braces: 3.0.2
-      chokidar: 3.5.3
-      connect: 3.7.0
-      di: 0.0.1
-      dom-serialize: 2.2.1
-      glob: 7.2.3
-      graceful-fs: 4.2.11
-      http-proxy: 1.18.1
-      isbinaryfile: 4.0.10
-      lodash: 4.17.21
-      log4js: 6.9.1
-      mime: 2.6.0
-      minimatch: 3.1.2
-      mkdirp: 0.5.6
-      qjobs: 1.2.0
-      range-parser: 1.2.1
-      rimraf: 3.0.2
-      socket.io: 4.7.2
-      source-map: 0.6.1
-      tmp: 0.2.1
-      ua-parser-js: 0.7.37
-      yargs: 16.2.0
-    transitivePeerDependencies:
-      - bufferutil
-      - debug
-      - supports-color
-      - utf-8-validate
-    dev: true
-
-  /kind-of@3.2.2:
-    resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-buffer: 1.1.6
-    dev: true
-
-  /kind-of@4.0.0:
-    resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-buffer: 1.1.6
-    dev: true
-
-  /kind-of@6.0.3:
-    resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /less@4.2.0:
-    resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==}
-    engines: {node: '>=6'}
-    hasBin: true
-    dependencies:
-      copy-anything: 2.0.6
-      parse-node-version: 1.0.1
-      tslib: 2.6.2
-    optionalDependencies:
-      errno: 0.1.8
-      graceful-fs: 4.2.11
-      image-size: 0.5.5
-      make-dir: 2.1.0
-      mime: 1.6.0
-      needle: 3.2.0
-      source-map: 0.6.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /lilconfig@2.0.5:
-    resolution: {integrity: sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /lines-and-columns@1.2.4:
-    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
-    dev: true
-
-  /lint-staged@12.5.0(enquirer@2.4.1):
-    resolution: {integrity: sha512-BKLUjWDsKquV/JuIcoQW4MSAI3ggwEImF1+sB4zaKvyVx1wBk3FsG7UK9bpnmBTN1pm7EH2BBcMwINJzCRv12g==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-    hasBin: true
-    dependencies:
-      cli-truncate: 3.1.0
-      colorette: 2.0.20
-      commander: 9.5.0
-      debug: 4.3.4(supports-color@9.4.0)
-      execa: 5.1.1
-      lilconfig: 2.0.5
-      listr2: 4.0.5(enquirer@2.4.1)
-      micromatch: 4.0.5
-      normalize-path: 3.0.0
-      object-inspect: 1.13.1
-      pidtree: 0.5.0
-      string-argv: 0.3.2
-      supports-color: 9.4.0
-      yaml: 1.10.2
-    transitivePeerDependencies:
-      - enquirer
-    dev: true
-
-  /listr2@4.0.5(enquirer@2.4.1):
-    resolution: {integrity: sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==}
-    engines: {node: '>=12'}
-    peerDependencies:
-      enquirer: '>= 2.3.0 < 3'
-    peerDependenciesMeta:
-      enquirer:
-        optional: true
-    dependencies:
-      cli-truncate: 2.1.0
-      colorette: 2.0.20
-      enquirer: 2.4.1
-      log-update: 4.0.0
-      p-map: 4.0.0
-      rfdc: 1.3.0
-      rxjs: 7.8.1
-      through: 2.3.8
-      wrap-ansi: 7.0.0
-    dev: true
-
-  /load-json-file@4.0.0:
-    resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==}
-    engines: {node: '>=4'}
-    dependencies:
-      graceful-fs: 4.2.11
-      parse-json: 4.0.0
-      pify: 3.0.0
-      strip-bom: 3.0.0
-    dev: true
-
-  /loader-runner@2.4.0:
-    resolution: {integrity: sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==}
-    engines: {node: '>=4.3.0 <5.0.0 || >=5.10'}
-    dev: true
-
-  /loader-utils@1.4.2:
-    resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==}
-    engines: {node: '>=4.0.0'}
-    dependencies:
-      big.js: 5.2.2
-      emojis-list: 3.0.0
-      json5: 1.0.2
-    dev: true
-
-  /loader-utils@3.2.1:
-    resolution: {integrity: sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==}
-    engines: {node: '>= 12.13.0'}
-    dev: true
-
-  /local-pkg@0.5.0:
-    resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
-    engines: {node: '>=14'}
-    dependencies:
-      mlly: 1.4.2
-      pkg-types: 1.0.3
-    dev: true
-
-  /locate-path@2.0.0:
-    resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==}
-    engines: {node: '>=4'}
-    dependencies:
-      p-locate: 2.0.0
-      path-exists: 3.0.0
-    dev: true
-
-  /locate-path@3.0.0:
-    resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
-    engines: {node: '>=6'}
-    dependencies:
-      p-locate: 3.0.0
-      path-exists: 3.0.0
-    dev: true
-
-  /locate-path@5.0.0:
-    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
-    engines: {node: '>=8'}
-    dependencies:
-      p-locate: 4.1.0
-    dev: true
-
-  /lodash._reinterpolate@3.0.0:
-    resolution: {integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==}
-    dev: false
-
-  /lodash.camelcase@4.3.0:
-    resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
-    dev: true
-
-  /lodash.get@4.4.2:
-    resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
-    dev: true
-
-  /lodash.isequal@4.5.0:
-    resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
-    dev: true
-
-  /lodash.ismatch@4.4.0:
-    resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==}
-    dev: true
-
-  /lodash.template@4.5.0:
-    resolution: {integrity: sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==}
-    dependencies:
-      lodash._reinterpolate: 3.0.0
-      lodash.templatesettings: 4.2.0
-    dev: false
-
-  /lodash.templatesettings@4.2.0:
-    resolution: {integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==}
-    dependencies:
-      lodash._reinterpolate: 3.0.0
-    dev: false
-
-  /lodash.uniq@4.5.0:
-    resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
-    dev: false
-
-  /lodash@4.17.21:
-    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
-    dev: true
-
-  /log-update@4.0.0:
-    resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==}
-    engines: {node: '>=10'}
-    dependencies:
-      ansi-escapes: 4.3.2
-      cli-cursor: 3.1.0
-      slice-ansi: 4.0.0
-      wrap-ansi: 6.2.0
-    dev: true
-
-  /log4js@6.9.1:
-    resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==}
-    engines: {node: '>=8.0'}
-    dependencies:
-      date-format: 4.0.14
-      debug: 4.3.4(supports-color@9.4.0)
-      flatted: 3.2.9
-      rfdc: 1.3.0
-      streamroller: 3.1.5
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /loupe@2.3.7:
-    resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
-    dependencies:
-      get-func-name: 2.0.2
-    dev: true
-
-  /lru-cache@4.1.5:
-    resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
-    dependencies:
-      pseudomap: 1.0.2
-      yallist: 2.1.2
-    dev: true
-
-  /lru-cache@5.1.1:
-    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
-    dependencies:
-      yallist: 3.1.1
-    dev: true
-
-  /lru-cache@6.0.0:
-    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
-    engines: {node: '>=10'}
-    dependencies:
-      yallist: 4.0.0
-    dev: true
-
-  /magic-string@0.25.9:
-    resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
-    dependencies:
-      sourcemap-codec: 1.4.8
-    dev: true
-
-  /magic-string@0.30.5:
-    resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==}
-    engines: {node: '>=12'}
-    dependencies:
-      '@jridgewell/sourcemap-codec': 1.4.15
-    dev: true
-
-  /make-dir@2.1.0:
-    resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
-    engines: {node: '>=6'}
-    dependencies:
-      pify: 4.0.1
-      semver: 5.7.2
-    dev: true
-
-  /make-dir@3.1.0:
-    resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
-    engines: {node: '>=8'}
-    dependencies:
-      semver: 6.3.1
-    dev: true
-
-  /make-error@1.3.6:
-    resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
-    dev: true
-
-  /map-cache@0.2.2:
-    resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /map-obj@1.0.1:
-    resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /map-obj@4.3.0:
-    resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /map-visit@1.0.0:
-    resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      object-visit: 1.0.1
-    dev: true
-
-  /marked@4.3.0:
-    resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==}
-    engines: {node: '>= 12'}
-    hasBin: true
-    dev: true
-
-  /md5.js@1.3.5:
-    resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==}
-    dependencies:
-      hash-base: 3.1.0
-      inherits: 2.0.4
-      safe-buffer: 5.2.1
-    dev: true
-
-  /media-typer@0.3.0:
-    resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
-    engines: {node: '>= 0.6'}
-    dev: true
-
-  /memory-fs@0.4.1:
-    resolution: {integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==}
-    dependencies:
-      errno: 0.1.8
-      readable-stream: 2.3.8
-    dev: true
-
-  /memory-fs@0.5.0:
-    resolution: {integrity: sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==}
-    engines: {node: '>=4.3.0 <5.0.0 || >=5.10'}
-    dependencies:
-      errno: 0.1.8
-      readable-stream: 2.3.8
-    dev: true
-
-  /meow@8.1.2:
-    resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==}
-    engines: {node: '>=10'}
-    dependencies:
-      '@types/minimist': 1.2.5
-      camelcase-keys: 6.2.2
-      decamelize-keys: 1.1.1
-      hard-rejection: 2.1.0
-      minimist-options: 4.1.0
-      normalize-package-data: 3.0.3
-      read-pkg-up: 7.0.1
-      redent: 3.0.0
-      trim-newlines: 3.0.1
-      type-fest: 0.18.1
-      yargs-parser: 20.2.9
-    dev: true
-
-  /merge-source-map@1.1.0:
-    resolution: {integrity: sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==}
-    dependencies:
-      source-map: 0.6.1
-    dev: true
-
-  /merge-stream@2.0.0:
-    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
-    dev: true
-
-  /micromatch@3.1.10:
-    resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-diff: 4.0.0
-      array-unique: 0.3.2
-      braces: 2.3.2
-      define-property: 2.0.2
-      extend-shallow: 3.0.2
-      extglob: 2.0.4
-      fragment-cache: 0.2.1
-      kind-of: 6.0.3
-      nanomatch: 1.2.13
-      object.pick: 1.3.0
-      regex-not: 1.0.2
-      snapdragon: 0.8.2
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /micromatch@4.0.5:
-    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
-    engines: {node: '>=8.6'}
-    dependencies:
-      braces: 3.0.2
-      picomatch: 2.3.1
-    dev: true
-
-  /miller-rabin@4.0.1:
-    resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==}
-    hasBin: true
-    dependencies:
-      bn.js: 4.12.0
-      brorand: 1.1.0
-    dev: true
-
-  /mime-db@1.52.0:
-    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
-    engines: {node: '>= 0.6'}
-    dev: true
-
-  /mime-types@2.1.35:
-    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
-    engines: {node: '>= 0.6'}
-    dependencies:
-      mime-db: 1.52.0
-    dev: true
-
-  /mime@1.6.0:
-    resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
-    engines: {node: '>=4'}
-    hasBin: true
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /mime@2.6.0:
-    resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
-    engines: {node: '>=4.0.0'}
-    hasBin: true
-    dev: true
-
-  /mimic-fn@2.1.0:
-    resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /mimic-fn@4.0.0:
-    resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /min-indent@1.0.1:
-    resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /minimalistic-assert@1.0.1:
-    resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
-    dev: true
-
-  /minimalistic-crypto-utils@1.0.1:
-    resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==}
-    dev: true
-
-  /minimatch@3.1.2:
-    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
-    dependencies:
-      brace-expansion: 1.1.11
-    dev: true
-
-  /minimist-options@4.1.0:
-    resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
-    engines: {node: '>= 6'}
-    dependencies:
-      arrify: 1.0.1
-      is-plain-obj: 1.1.0
-      kind-of: 6.0.3
-    dev: true
-
-  /minimist@1.2.8:
-    resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
-    dev: true
-
-  /mississippi@3.0.0:
-    resolution: {integrity: sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==}
-    engines: {node: '>=4.0.0'}
-    dependencies:
-      concat-stream: 1.6.2
-      duplexify: 3.7.1
-      end-of-stream: 1.4.4
-      flush-write-stream: 1.1.1
-      from2: 2.3.0
-      parallel-transform: 1.2.0
-      pump: 3.0.0
-      pumpify: 1.5.1
-      stream-each: 1.2.3
-      through2: 2.0.5
-    dev: true
-
-  /mixin-deep@1.3.2:
-    resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      for-in: 1.0.2
-      is-extendable: 1.0.1
-    dev: true
-
-  /mkdirp-classic@0.5.3:
-    resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
-    dev: true
-
-  /mkdirp@0.5.6:
-    resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
-    hasBin: true
-    dependencies:
-      minimist: 1.2.8
-    dev: true
-
-  /mlly@1.4.2:
-    resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
-    dependencies:
-      acorn: 8.11.2
-      pathe: 1.1.1
-      pkg-types: 1.0.3
-      ufo: 1.3.2
-    dev: true
-
-  /modify-values@1.0.1:
-    resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /move-concurrently@1.0.1:
-    resolution: {integrity: sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==}
-    dependencies:
-      aproba: 1.2.0
-      copy-concurrently: 1.0.5
-      fs-write-stream-atomic: 1.0.10
-      mkdirp: 0.5.6
-      rimraf: 2.7.1
-      run-queue: 1.0.3
-    dev: true
-
-  /ms@2.0.0:
-    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
-    dev: true
-
-  /ms@2.1.2:
-    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
-    dev: true
-
-  /ms@2.1.3:
-    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /nan@2.18.0:
-    resolution: {integrity: sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==}
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /nanoid@3.3.7:
-    resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
-    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
-    hasBin: true
-
-  /nanomatch@1.2.13:
-    resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-diff: 4.0.0
-      array-unique: 0.3.2
-      define-property: 2.0.2
-      extend-shallow: 3.0.2
-      fragment-cache: 0.2.1
-      is-windows: 1.0.2
-      kind-of: 6.0.3
-      object.pick: 1.3.0
-      regex-not: 1.0.2
-      snapdragon: 0.8.2
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /needle@3.2.0:
-    resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==}
-    engines: {node: '>= 4.4.x'}
-    hasBin: true
-    requiresBuild: true
-    dependencies:
-      debug: 3.2.7
-      iconv-lite: 0.6.3
-      sax: 1.3.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-    optional: true
-
-  /negotiator@0.6.3:
-    resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
-    engines: {node: '>= 0.6'}
-    dev: true
-
-  /neo-async@2.6.2:
-    resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
-    dev: true
-
-  /node-fetch@2.6.7:
-    resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
-    engines: {node: 4.x || >=6.0.0}
-    peerDependencies:
-      encoding: ^0.1.0
-    peerDependenciesMeta:
-      encoding:
-        optional: true
-    dependencies:
-      whatwg-url: 5.0.0
-    dev: true
-
-  /node-libs-browser@2.2.1:
-    resolution: {integrity: sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==}
-    dependencies:
-      assert: 1.5.1
-      browserify-zlib: 0.2.0
-      buffer: 4.9.2
-      console-browserify: 1.2.0
-      constants-browserify: 1.0.0
-      crypto-browserify: 3.12.0
-      domain-browser: 1.2.0
-      events: 3.3.0
-      https-browserify: 1.0.0
-      os-browserify: 0.3.0
-      path-browserify: 0.0.1
-      process: 0.11.10
-      punycode: 1.4.1
-      querystring-es3: 0.2.1
-      readable-stream: 2.3.8
-      stream-browserify: 2.0.2
-      stream-http: 2.8.3
-      string_decoder: 1.3.0
-      timers-browserify: 2.0.12
-      tty-browserify: 0.0.0
-      url: 0.11.3
-      util: 0.11.1
-      vm-browserify: 1.1.2
-    dev: true
-
-  /normalize-package-data@2.5.0:
-    resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
-    dependencies:
-      hosted-git-info: 2.8.9
-      resolve: 1.22.8
-      semver: 5.7.2
-      validate-npm-package-license: 3.0.4
-    dev: true
-
-  /normalize-package-data@3.0.3:
-    resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==}
-    engines: {node: '>=10'}
-    dependencies:
-      hosted-git-info: 4.1.0
-      is-core-module: 2.13.1
-      semver: 7.5.4
-      validate-npm-package-license: 3.0.4
-    dev: true
-
-  /normalize-path@1.0.0:
-    resolution: {integrity: sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /normalize-path@2.1.1:
-    resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==}
-    engines: {node: '>=0.10.0'}
-    requiresBuild: true
-    dependencies:
-      remove-trailing-separator: 1.1.0
-    dev: true
-    optional: true
-
-  /normalize-path@3.0.0:
-    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /npm-run-path@2.0.2:
-    resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
-    engines: {node: '>=4'}
-    dependencies:
-      path-key: 2.0.1
-    dev: true
-
-  /npm-run-path@4.0.1:
-    resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
-    engines: {node: '>=8'}
-    dependencies:
-      path-key: 3.1.1
-    dev: true
-
-  /npm-run-path@5.1.0:
-    resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
-    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-    dependencies:
-      path-key: 4.0.0
-    dev: true
-
-  /nwsapi@2.2.7:
-    resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
-    dev: true
-
-  /object-assign@4.1.1:
-    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /object-copy@0.1.0:
-    resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      copy-descriptor: 0.1.1
-      define-property: 0.2.5
-      kind-of: 3.2.2
-    dev: true
-
-  /object-inspect@1.13.1:
-    resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
-    dev: true
-
-  /object-keys@1.1.1:
-    resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /object-visit@1.0.1:
-    resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      isobject: 3.0.1
-    dev: true
-
-  /object.assign@4.1.5:
-    resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.5
-      define-properties: 1.2.1
-      has-symbols: 1.0.3
-      object-keys: 1.1.1
-    dev: true
-
-  /object.pick@1.3.0:
-    resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      isobject: 3.0.1
-    dev: true
-
-  /on-finished@2.3.0:
-    resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      ee-first: 1.1.1
-    dev: true
-
-  /on-finished@2.4.1:
-    resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      ee-first: 1.1.1
-    dev: true
-
-  /once@1.4.0:
-    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
-    dependencies:
-      wrappy: 1.0.2
-    dev: true
-
-  /onetime@5.1.2:
-    resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
-    engines: {node: '>=6'}
-    dependencies:
-      mimic-fn: 2.1.0
-    dev: true
-
-  /onetime@6.0.0:
-    resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
-    engines: {node: '>=12'}
-    dependencies:
-      mimic-fn: 4.0.0
-    dev: true
-
-  /os-browserify@0.3.0:
-    resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==}
-    dev: true
-
-  /p-finally@1.0.0:
-    resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /p-limit@1.3.0:
-    resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==}
-    engines: {node: '>=4'}
-    dependencies:
-      p-try: 1.0.0
-    dev: true
-
-  /p-limit@2.3.0:
-    resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
-    engines: {node: '>=6'}
-    dependencies:
-      p-try: 2.2.0
-    dev: true
-
-  /p-limit@5.0.0:
-    resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==}
-    engines: {node: '>=18'}
-    dependencies:
-      yocto-queue: 1.0.0
-    dev: true
-
-  /p-locate@2.0.0:
-    resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==}
-    engines: {node: '>=4'}
-    dependencies:
-      p-limit: 1.3.0
-    dev: true
-
-  /p-locate@3.0.0:
-    resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
-    engines: {node: '>=6'}
-    dependencies:
-      p-limit: 2.3.0
-    dev: true
-
-  /p-locate@4.1.0:
-    resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
-    engines: {node: '>=8'}
-    dependencies:
-      p-limit: 2.3.0
-    dev: true
-
-  /p-map@4.0.0:
-    resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      aggregate-error: 3.1.0
-    dev: true
-
-  /p-try@1.0.0:
-    resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /p-try@2.2.0:
-    resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /pako@1.0.11:
-    resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
-    dev: true
-
-  /parallel-transform@1.2.0:
-    resolution: {integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==}
-    dependencies:
-      cyclist: 1.0.2
-      inherits: 2.0.4
-      readable-stream: 2.3.8
-    dev: true
-
-  /parse-asn1@5.1.6:
-    resolution: {integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==}
-    dependencies:
-      asn1.js: 5.4.1
-      browserify-aes: 1.2.0
-      evp_bytestokey: 1.0.3
-      pbkdf2: 3.1.2
-      safe-buffer: 5.2.1
-    dev: true
-
-  /parse-json@4.0.0:
-    resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==}
-    engines: {node: '>=4'}
-    dependencies:
-      error-ex: 1.3.2
-      json-parse-better-errors: 1.0.2
-    dev: true
-
-  /parse-json@5.2.0:
-    resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
-    engines: {node: '>=8'}
-    dependencies:
-      '@babel/code-frame': 7.23.5
-      error-ex: 1.3.2
-      json-parse-even-better-errors: 2.3.1
-      lines-and-columns: 1.2.4
-    dev: true
-
-  /parse-node-version@1.0.1:
-    resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
-    engines: {node: '>= 0.10'}
-    dev: true
-
-  /parse5@6.0.1:
-    resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
-    dev: true
-
-  /parseurl@1.3.3:
-    resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
-    engines: {node: '>= 0.8'}
-    dev: true
-
-  /pascalcase@0.1.1:
-    resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /path-browserify@0.0.1:
-    resolution: {integrity: sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==}
-    dev: true
-
-  /path-dirname@1.0.2:
-    resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==}
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /path-exists@3.0.0:
-    resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /path-exists@4.0.0:
-    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /path-is-absolute@1.0.1:
-    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /path-key@2.0.1:
-    resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /path-key@3.1.1:
-    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /path-key@4.0.0:
-    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /path-parse@1.0.7:
-    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
-
-  /path-type@3.0.0:
-    resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==}
-    engines: {node: '>=4'}
-    dependencies:
-      pify: 3.0.0
-    dev: true
-
-  /pathe@1.1.1:
-    resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==}
-    dev: true
-
-  /pathval@1.1.1:
-    resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
-    dev: true
-
-  /pbkdf2@3.1.2:
-    resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==}
-    engines: {node: '>=0.12'}
-    dependencies:
-      create-hash: 1.2.0
-      create-hmac: 1.1.7
-      ripemd160: 2.0.2
-      safe-buffer: 5.2.1
-      sha.js: 2.4.11
-    dev: true
-
-  /pend@1.2.0:
-    resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
-    dev: true
-
-  /picocolors@1.0.0:
-    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
-
-  /picomatch@2.3.1:
-    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
-    engines: {node: '>=8.6'}
-    dev: true
-
-  /pidtree@0.5.0:
-    resolution: {integrity: sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==}
-    engines: {node: '>=0.10'}
-    hasBin: true
-    dev: true
-
-  /pify@2.3.0:
-    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /pify@3.0.0:
-    resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /pify@4.0.1:
-    resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
-    engines: {node: '>=6'}
-    requiresBuild: true
-    dev: true
-
-  /pkg-dir@3.0.0:
-    resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==}
-    engines: {node: '>=6'}
-    dependencies:
-      find-up: 3.0.0
-    dev: true
-
-  /pkg-dir@4.2.0:
-    resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      find-up: 4.1.0
-    dev: true
-
-  /pkg-types@1.0.3:
-    resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
-    dependencies:
-      jsonc-parser: 3.2.0
-      mlly: 1.4.2
-      pathe: 1.1.1
-    dev: true
-
-  /posix-character-classes@0.1.1:
-    resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /postcss-modules-extract-imports@3.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==}
-    engines: {node: ^10 || ^12 || >= 14}
-    peerDependencies:
-      postcss: ^8.1.0
-    dependencies:
-      postcss: 8.4.32
-    dev: true
-
-  /postcss-modules-local-by-default@4.0.3(postcss@8.4.32):
-    resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==}
-    engines: {node: ^10 || ^12 || >= 14}
-    peerDependencies:
-      postcss: ^8.1.0
-    dependencies:
-      icss-utils: 5.1.0(postcss@8.4.32)
-      postcss: 8.4.32
-      postcss-selector-parser: 6.0.13
-      postcss-value-parser: 4.2.0
-    dev: true
-
-  /postcss-modules-scope@3.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==}
-    engines: {node: ^10 || ^12 || >= 14}
-    peerDependencies:
-      postcss: ^8.1.0
-    dependencies:
-      postcss: 8.4.32
-      postcss-selector-parser: 6.0.13
-    dev: true
-
-  /postcss-modules-values@4.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==}
-    engines: {node: ^10 || ^12 || >= 14}
-    peerDependencies:
-      postcss: ^8.1.0
-    dependencies:
-      icss-utils: 5.1.0(postcss@8.4.32)
-      postcss: 8.4.32
-    dev: true
-
-  /postcss-modules@4.3.1(postcss@8.4.32):
-    resolution: {integrity: sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==}
-    peerDependencies:
-      postcss: ^8.0.0
-    dependencies:
-      generic-names: 4.0.0
-      icss-replace-symbols: 1.1.0
-      lodash.camelcase: 4.3.0
-      postcss: 8.4.32
-      postcss-modules-extract-imports: 3.0.0(postcss@8.4.32)
-      postcss-modules-local-by-default: 4.0.3(postcss@8.4.32)
-      postcss-modules-scope: 3.0.0(postcss@8.4.32)
-      postcss-modules-values: 4.0.0(postcss@8.4.32)
-      string-hash: 1.1.3
-    dev: true
-
-  /postcss-selector-parser@6.0.13:
-    resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
-    engines: {node: '>=4'}
-    dependencies:
-      cssesc: 3.0.0
-      util-deprecate: 1.0.2
-    dev: true
-
-  /postcss-value-parser@4.2.0:
-    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
-    dev: true
-
-  /postcss@8.4.32:
-    resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==}
-    engines: {node: ^10 || ^12 || >=14}
-    dependencies:
-      nanoid: 3.3.7
-      picocolors: 1.0.0
-      source-map-js: 1.0.2
-
-  /prettier@2.8.8:
-    resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
-    engines: {node: '>=10.13.0'}
-    hasBin: true
-
-  /pretty-format@29.7.0:
-    resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    dependencies:
-      '@jest/schemas': 29.6.3
-      ansi-styles: 5.2.0
-      react-is: 18.2.0
-    dev: true
-
-  /process-nextick-args@2.0.1:
-    resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
-    dev: true
-
-  /process@0.11.10:
-    resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
-    engines: {node: '>= 0.6.0'}
-    dev: true
-
-  /progress@2.0.3:
-    resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
-    engines: {node: '>=0.4.0'}
-    dev: true
-
-  /promise-inflight@1.0.1(bluebird@3.7.2):
-    resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
-    peerDependencies:
-      bluebird: '*'
-    peerDependenciesMeta:
-      bluebird:
-        optional: true
-    dependencies:
-      bluebird: 3.7.2
-    dev: true
-
-  /promise@7.3.1:
-    resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==}
-    dependencies:
-      asap: 2.0.6
-    dev: true
-
-  /proxy-from-env@1.1.0:
-    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
-    dev: true
-
-  /prr@1.0.1:
-    resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
-    dev: true
-
-  /pseudomap@1.0.2:
-    resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
-    dev: true
-
-  /psl@1.9.0:
-    resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
-    dev: true
-
-  /public-encrypt@4.0.3:
-    resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==}
-    dependencies:
-      bn.js: 4.12.0
-      browserify-rsa: 4.1.0
-      create-hash: 1.2.0
-      parse-asn1: 5.1.6
-      randombytes: 2.1.0
-      safe-buffer: 5.2.1
-    dev: true
-
-  /pug-attrs@3.0.0:
-    resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==}
-    dependencies:
-      constantinople: 4.0.1
-      js-stringify: 1.0.2
-      pug-runtime: 3.0.1
-    dev: true
-
-  /pug-code-gen@3.0.2:
-    resolution: {integrity: sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==}
-    dependencies:
-      constantinople: 4.0.1
-      doctypes: 1.1.0
-      js-stringify: 1.0.2
-      pug-attrs: 3.0.0
-      pug-error: 2.0.0
-      pug-runtime: 3.0.1
-      void-elements: 3.1.0
-      with: 7.0.2
-    dev: true
-
-  /pug-error@2.0.0:
-    resolution: {integrity: sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==}
-    dev: true
-
-  /pug-filters@4.0.0:
-    resolution: {integrity: sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==}
-    dependencies:
-      constantinople: 4.0.1
-      jstransformer: 1.0.0
-      pug-error: 2.0.0
-      pug-walk: 2.0.0
-      resolve: 1.22.8
-    dev: true
-
-  /pug-lexer@5.0.1:
-    resolution: {integrity: sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==}
-    dependencies:
-      character-parser: 2.2.0
-      is-expression: 4.0.0
-      pug-error: 2.0.0
-    dev: true
-
-  /pug-linker@4.0.0:
-    resolution: {integrity: sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==}
-    dependencies:
-      pug-error: 2.0.0
-      pug-walk: 2.0.0
-    dev: true
-
-  /pug-load@3.0.0:
-    resolution: {integrity: sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==}
-    dependencies:
-      object-assign: 4.1.1
-      pug-walk: 2.0.0
-    dev: true
-
-  /pug-parser@6.0.0:
-    resolution: {integrity: sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==}
-    dependencies:
-      pug-error: 2.0.0
-      token-stream: 1.0.0
-    dev: true
-
-  /pug-runtime@3.0.1:
-    resolution: {integrity: sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==}
-    dev: true
-
-  /pug-strip-comments@2.0.0:
-    resolution: {integrity: sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==}
-    dependencies:
-      pug-error: 2.0.0
-    dev: true
-
-  /pug-walk@2.0.0:
-    resolution: {integrity: sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==}
-    dev: true
-
-  /pug@3.0.2:
-    resolution: {integrity: sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==}
-    dependencies:
-      pug-code-gen: 3.0.2
-      pug-filters: 4.0.0
-      pug-lexer: 5.0.1
-      pug-linker: 4.0.0
-      pug-load: 3.0.0
-      pug-parser: 6.0.0
-      pug-runtime: 3.0.1
-      pug-strip-comments: 2.0.0
-    dev: true
-
-  /pump@2.0.1:
-    resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==}
-    dependencies:
-      end-of-stream: 1.4.4
-      once: 1.4.0
-    dev: true
-
-  /pump@3.0.0:
-    resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
-    dependencies:
-      end-of-stream: 1.4.4
-      once: 1.4.0
-    dev: true
-
-  /pumpify@1.5.1:
-    resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==}
-    dependencies:
-      duplexify: 3.7.1
-      inherits: 2.0.4
-      pump: 2.0.1
-    dev: true
-
-  /punycode@1.4.1:
-    resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
-    dev: true
-
-  /punycode@2.3.1:
-    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /puppeteer@14.4.1:
-    resolution: {integrity: sha512-+H0Gm84aXUvSLdSiDROtLlOofftClgw2TdceMvvCU9UvMryappoeS3+eOLfKvoy4sm8B8MWnYmPhWxVFudAOFQ==}
-    engines: {node: '>=14.1.0'}
-    deprecated: < 21.3.7 is no longer supported
-    requiresBuild: true
-    dependencies:
-      cross-fetch: 3.1.5
-      debug: 4.3.4(supports-color@9.4.0)
-      devtools-protocol: 0.0.1001819
-      extract-zip: 2.0.1
-      https-proxy-agent: 5.0.1
-      pkg-dir: 4.2.0
-      progress: 2.0.3
-      proxy-from-env: 1.1.0
-      rimraf: 3.0.2
-      tar-fs: 2.1.1
-      unbzip2-stream: 1.4.3
-      ws: 8.7.0
-    transitivePeerDependencies:
-      - bufferutil
-      - encoding
-      - supports-color
-      - utf-8-validate
-    dev: true
-
-  /q@1.5.1:
-    resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==}
-    engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
-    dev: true
-
-  /qjobs@1.2.0:
-    resolution: {integrity: sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==}
-    engines: {node: '>=0.9'}
-    dev: true
-
-  /qs@6.11.0:
-    resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
-    engines: {node: '>=0.6'}
-    dependencies:
-      side-channel: 1.0.4
-    dev: true
-
-  /qs@6.11.2:
-    resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==}
-    engines: {node: '>=0.6'}
-    dependencies:
-      side-channel: 1.0.4
-    dev: true
-
-  /querystring-es3@0.2.1:
-    resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==}
-    engines: {node: '>=0.4.x'}
-    dev: true
-
-  /querystringify@2.2.0:
-    resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
-    dev: true
-
-  /quick-lru@4.0.1:
-    resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /randombytes@2.1.0:
-    resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
-    dependencies:
-      safe-buffer: 5.2.1
-
-  /randomfill@1.0.4:
-    resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==}
-    dependencies:
-      randombytes: 2.1.0
-      safe-buffer: 5.2.1
-    dev: true
-
-  /range-parser@1.2.1:
-    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
-    engines: {node: '>= 0.6'}
-    dev: true
-
-  /raw-body@2.5.2:
-    resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      bytes: 3.1.2
-      http-errors: 2.0.0
-      iconv-lite: 0.4.24
-      unpipe: 1.0.0
-    dev: true
-
-  /react-is@18.2.0:
-    resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
-    dev: true
-
-  /read-pkg-up@3.0.0:
-    resolution: {integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==}
-    engines: {node: '>=4'}
-    dependencies:
-      find-up: 2.1.0
-      read-pkg: 3.0.0
-    dev: true
-
-  /read-pkg-up@7.0.1:
-    resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
-    engines: {node: '>=8'}
-    dependencies:
-      find-up: 4.1.0
-      read-pkg: 5.2.0
-      type-fest: 0.8.1
-    dev: true
-
-  /read-pkg@3.0.0:
-    resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==}
-    engines: {node: '>=4'}
-    dependencies:
-      load-json-file: 4.0.0
-      normalize-package-data: 2.5.0
-      path-type: 3.0.0
-    dev: true
-
-  /read-pkg@5.2.0:
-    resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==}
-    engines: {node: '>=8'}
-    dependencies:
-      '@types/normalize-package-data': 2.4.4
-      normalize-package-data: 2.5.0
-      parse-json: 5.2.0
-      type-fest: 0.6.0
-    dev: true
-
-  /readable-stream@2.3.8:
-    resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
-    dependencies:
-      core-util-is: 1.0.3
-      inherits: 2.0.4
-      isarray: 1.0.0
-      process-nextick-args: 2.0.1
-      safe-buffer: 5.1.2
-      string_decoder: 1.1.1
-      util-deprecate: 1.0.2
-    dev: true
-
-  /readable-stream@3.6.2:
-    resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
-    engines: {node: '>= 6'}
-    dependencies:
-      inherits: 2.0.4
-      string_decoder: 1.3.0
-      util-deprecate: 1.0.2
-    dev: true
-
-  /readdirp@2.2.1:
-    resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==}
-    engines: {node: '>=0.10'}
-    requiresBuild: true
-    dependencies:
-      graceful-fs: 4.2.11
-      micromatch: 3.1.10
-      readable-stream: 2.3.8
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-    optional: true
-
-  /readdirp@3.6.0:
-    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
-    engines: {node: '>=8.10.0'}
-    dependencies:
-      picomatch: 2.3.1
-    dev: true
-
-  /rechoir@0.6.2:
-    resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==}
-    engines: {node: '>= 0.10'}
-    dependencies:
-      resolve: 1.22.8
-    dev: true
-
-  /redent@3.0.0:
-    resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
-    engines: {node: '>=8'}
-    dependencies:
-      indent-string: 4.0.0
-      strip-indent: 3.0.0
-    dev: true
-
-  /regex-not@1.0.2:
-    resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      extend-shallow: 3.0.2
-      safe-regex: 1.1.0
-    dev: true
-
-  /remove-trailing-separator@1.1.0:
-    resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==}
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /repeat-element@1.1.4:
-    resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /repeat-string@1.6.1:
-    resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==}
-    engines: {node: '>=0.10'}
-    dev: true
-
-  /require-directory@2.1.1:
-    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /requires-port@1.0.0:
-    resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
-    dev: true
-
-  /resolve-url@0.2.1:
-    resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==}
-    deprecated: https://github.com/lydell/resolve-url#deprecated
-    dev: true
-
-  /resolve@1.19.0:
-    resolution: {integrity: sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==}
-    dependencies:
-      is-core-module: 2.13.1
-      path-parse: 1.0.7
-    dev: true
-
-  /resolve@1.22.8:
-    resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
-    hasBin: true
-    dependencies:
-      is-core-module: 2.13.1
-      path-parse: 1.0.7
-      supports-preserve-symlinks-flag: 1.0.0
-
-  /restore-cursor@3.1.0:
-    resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
-    engines: {node: '>=8'}
-    dependencies:
-      onetime: 5.1.2
-      signal-exit: 3.0.7
-    dev: true
-
-  /ret@0.1.15:
-    resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==}
-    engines: {node: '>=0.12'}
-    dev: true
-
-  /rfdc@1.3.0:
-    resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
-    dev: true
-
-  /rimraf@2.7.1:
-    resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
-    hasBin: true
-    dependencies:
-      glob: 7.2.3
-    dev: true
-
-  /rimraf@3.0.2:
-    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
-    hasBin: true
-    dependencies:
-      glob: 7.2.3
-    dev: true
-
-  /ripemd160@2.0.2:
-    resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==}
-    dependencies:
-      hash-base: 3.1.0
-      inherits: 2.0.4
-    dev: true
-
-  /rollup-plugin-typescript2@0.32.1(rollup@2.79.1)(typescript@4.9.5):
-    resolution: {integrity: sha512-RanO8bp1WbeMv0bVlgcbsFNCn+Y3rX7wF97SQLDxf0fMLsg0B/QFF005t4AsGUcDgF3aKJHoqt4JF2xVaABeKw==}
-    peerDependencies:
-      rollup: '>=1.26.3'
-      typescript: '>=2.4.0'
-    dependencies:
-      '@rollup/pluginutils': 4.2.1
-      find-cache-dir: 3.3.2
-      fs-extra: 10.1.0
-      resolve: 1.22.8
-      rollup: 2.79.1
-      tslib: 2.6.2
-      typescript: 4.9.5
-    dev: true
-
-  /rollup@2.79.1:
-    resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
-    engines: {node: '>=10.0.0'}
-    hasBin: true
-    optionalDependencies:
-      fsevents: 2.3.3
-    dev: true
-
-  /rollup@4.6.1:
-    resolution: {integrity: sha512-jZHaZotEHQaHLgKr8JnQiDT1rmatjgKlMekyksz+yk9jt/8z9quNjnKNRoaM0wd9DC2QKXjmWWuDYtM3jfF8pQ==}
-    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
-    hasBin: true
-    optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.6.1
-      '@rollup/rollup-android-arm64': 4.6.1
-      '@rollup/rollup-darwin-arm64': 4.6.1
-      '@rollup/rollup-darwin-x64': 4.6.1
-      '@rollup/rollup-linux-arm-gnueabihf': 4.6.1
-      '@rollup/rollup-linux-arm64-gnu': 4.6.1
-      '@rollup/rollup-linux-arm64-musl': 4.6.1
-      '@rollup/rollup-linux-x64-gnu': 4.6.1
-      '@rollup/rollup-linux-x64-musl': 4.6.1
-      '@rollup/rollup-win32-arm64-msvc': 4.6.1
-      '@rollup/rollup-win32-ia32-msvc': 4.6.1
-      '@rollup/rollup-win32-x64-msvc': 4.6.1
-      fsevents: 2.3.3
-    dev: true
-
-  /run-queue@1.0.3:
-    resolution: {integrity: sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==}
-    dependencies:
-      aproba: 1.2.0
-    dev: true
-
-  /rxjs@7.8.1:
-    resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
-    dependencies:
-      tslib: 2.6.2
-    dev: true
-
-  /safe-buffer@5.1.2:
-    resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
-    dev: true
-
-  /safe-buffer@5.2.1:
-    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
-
-  /safe-regex@1.1.0:
-    resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==}
-    dependencies:
-      ret: 0.1.15
-    dev: true
-
-  /safer-buffer@2.1.2:
-    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
-    dev: true
-
-  /sass@1.69.5:
-    resolution: {integrity: sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==}
-    engines: {node: '>=14.0.0'}
-    hasBin: true
-    dependencies:
-      chokidar: 3.5.3
-      immutable: 4.3.4
-      source-map-js: 1.0.2
-    dev: true
-
-  /sax@1.2.4:
-    resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
-    dev: true
-
-  /sax@1.3.0:
-    resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==}
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /saxes@5.0.1:
-    resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==}
-    engines: {node: '>=10'}
-    dependencies:
-      xmlchars: 2.2.0
-    dev: true
-
-  /schema-utils@1.0.0:
-    resolution: {integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==}
-    engines: {node: '>= 4'}
-    dependencies:
-      ajv: 6.12.6
-      ajv-errors: 1.0.1(ajv@6.12.6)
-      ajv-keywords: 3.5.2(ajv@6.12.6)
-    dev: true
-
-  /semver@5.7.2:
-    resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
-    hasBin: true
-    dev: true
-
-  /semver@6.3.1:
-    resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
-    hasBin: true
-    dev: true
-
-  /semver@7.5.4:
-    resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      lru-cache: 6.0.0
-    dev: true
-
-  /serialize-javascript@4.0.0:
-    resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==}
-    dependencies:
-      randombytes: 2.1.0
-    dev: true
-
-  /serialize-javascript@6.0.1:
-    resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==}
-    dependencies:
-      randombytes: 2.1.0
-    dev: false
-
-  /set-function-length@1.1.1:
-    resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      define-data-property: 1.1.1
-      get-intrinsic: 1.2.2
-      gopd: 1.0.1
-      has-property-descriptors: 1.0.1
-    dev: true
-
-  /set-value@2.0.1:
-    resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      extend-shallow: 2.0.1
-      is-extendable: 0.1.1
-      is-plain-object: 2.0.4
-      split-string: 3.1.0
-    dev: true
-
-  /setimmediate@1.0.5:
-    resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
-    dev: true
-
-  /setprototypeof@1.2.0:
-    resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
-    dev: true
-
-  /sha.js@2.4.11:
-    resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
-    hasBin: true
-    dependencies:
-      inherits: 2.0.4
-      safe-buffer: 5.2.1
-    dev: true
-
-  /shebang-command@1.2.0:
-    resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      shebang-regex: 1.0.0
-    dev: true
-
-  /shebang-command@2.0.0:
-    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
-    engines: {node: '>=8'}
-    dependencies:
-      shebang-regex: 3.0.0
-    dev: true
-
-  /shebang-regex@1.0.0:
-    resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /shebang-regex@3.0.0:
-    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /shelljs@0.8.5:
-    resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dependencies:
-      glob: 7.2.3
-      interpret: 1.4.0
-      rechoir: 0.6.2
-    dev: true
-
-  /side-channel@1.0.4:
-    resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
-    dependencies:
-      call-bind: 1.0.5
-      get-intrinsic: 1.2.2
-      object-inspect: 1.13.1
-    dev: true
-
-  /siginfo@2.0.0:
-    resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
-    dev: true
-
-  /signal-exit@3.0.7:
-    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
-    dev: true
-
-  /signal-exit@4.1.0:
-    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
-    engines: {node: '>=14'}
-    dev: true
-
-  /slash@3.0.0:
-    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /slice-ansi@3.0.0:
-    resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      ansi-styles: 4.3.0
-      astral-regex: 2.0.0
-      is-fullwidth-code-point: 3.0.0
-    dev: true
-
-  /slice-ansi@4.0.0:
-    resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      ansi-styles: 4.3.0
-      astral-regex: 2.0.0
-      is-fullwidth-code-point: 3.0.0
-    dev: true
-
-  /slice-ansi@5.0.0:
-    resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
-    engines: {node: '>=12'}
-    dependencies:
-      ansi-styles: 6.2.1
-      is-fullwidth-code-point: 4.0.0
-    dev: true
-
-  /snapdragon-node@2.1.1:
-    resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      define-property: 1.0.0
-      isobject: 3.0.1
-      snapdragon-util: 3.0.1
-    dev: true
-
-  /snapdragon-util@3.0.1:
-    resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      kind-of: 3.2.2
-    dev: true
-
-  /snapdragon@0.8.2:
-    resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      base: 0.11.2
-      debug: 2.6.9
-      define-property: 0.2.5
-      extend-shallow: 2.0.1
-      map-cache: 0.2.2
-      source-map: 0.5.6
-      source-map-resolve: 0.5.3
-      use: 3.1.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /socket.io-adapter@2.5.2:
-    resolution: {integrity: sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==}
-    dependencies:
-      ws: 8.11.0
-    transitivePeerDependencies:
-      - bufferutil
-      - utf-8-validate
-    dev: true
-
-  /socket.io-parser@4.2.4:
-    resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
-    engines: {node: '>=10.0.0'}
-    dependencies:
-      '@socket.io/component-emitter': 3.1.0
-      debug: 4.3.4(supports-color@9.4.0)
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /socket.io@4.7.2:
-    resolution: {integrity: sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==}
-    engines: {node: '>=10.2.0'}
-    dependencies:
-      accepts: 1.3.8
-      base64id: 2.0.0
-      cors: 2.8.5
-      debug: 4.3.4(supports-color@9.4.0)
-      engine.io: 6.5.4
-      socket.io-adapter: 2.5.2
-      socket.io-parser: 4.2.4
-    transitivePeerDependencies:
-      - bufferutil
-      - supports-color
-      - utf-8-validate
-    dev: true
-
-  /source-list-map@2.0.1:
-    resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==}
-    dev: true
-
-  /source-map-js@1.0.2:
-    resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
-    engines: {node: '>=0.10.0'}
-
-  /source-map-resolve@0.5.3:
-    resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==}
-    deprecated: See https://github.com/lydell/source-map-resolve#deprecated
-    dependencies:
-      atob: 2.1.2
-      decode-uri-component: 0.2.2
-      resolve-url: 0.2.1
-      source-map-url: 0.4.1
-      urix: 0.1.0
-    dev: true
-
-  /source-map-resolve@0.6.0:
-    resolution: {integrity: sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==}
-    deprecated: See https://github.com/lydell/source-map-resolve#deprecated
-    dependencies:
-      atob: 2.1.2
-      decode-uri-component: 0.2.2
-    dev: true
-
-  /source-map-support@0.5.21:
-    resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
-    dependencies:
-      buffer-from: 1.1.2
-      source-map: 0.6.1
-    dev: true
-
-  /source-map-url@0.4.1:
-    resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==}
-    deprecated: See https://github.com/lydell/source-map-url#deprecated
-    dev: true
-
-  /source-map@0.5.6:
-    resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==}
-    engines: {node: '>=0.10.0'}
-
-  /source-map@0.6.1:
-    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
-    engines: {node: '>=0.10.0'}
-
-  /source-map@0.7.4:
-    resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
-    engines: {node: '>= 8'}
-    dev: true
-
-  /sourcemap-codec@1.4.8:
-    resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
-    deprecated: Please use @jridgewell/sourcemap-codec instead
-    dev: true
-
-  /spdx-correct@3.2.0:
-    resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
-    dependencies:
-      spdx-expression-parse: 3.0.1
-      spdx-license-ids: 3.0.16
-    dev: true
-
-  /spdx-exceptions@2.3.0:
-    resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
-    dev: true
-
-  /spdx-expression-parse@3.0.1:
-    resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
-    dependencies:
-      spdx-exceptions: 2.3.0
-      spdx-license-ids: 3.0.16
-    dev: true
-
-  /spdx-license-ids@3.0.16:
-    resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==}
-    dev: true
-
-  /split-string@3.1.0:
-    resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      extend-shallow: 3.0.2
-    dev: true
-
-  /split2@3.2.2:
-    resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==}
-    dependencies:
-      readable-stream: 3.6.2
-    dev: true
-
-  /split@1.0.1:
-    resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==}
-    dependencies:
-      through: 2.3.8
-    dev: true
-
-  /sprintf-js@1.0.3:
-    resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
-    dev: true
-
-  /ssri@6.0.2:
-    resolution: {integrity: sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==}
-    dependencies:
-      figgy-pudding: 3.5.2
-    dev: true
-
-  /stackback@0.0.2:
-    resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
-    dev: true
-
-  /static-extend@0.1.2:
-    resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      define-property: 0.2.5
-      object-copy: 0.1.0
-    dev: true
-
-  /statuses@1.5.0:
-    resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
-    engines: {node: '>= 0.6'}
-    dev: true
-
-  /statuses@2.0.1:
-    resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
-    engines: {node: '>= 0.8'}
-    dev: true
-
-  /std-env@3.6.0:
-    resolution: {integrity: sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==}
-    dev: true
-
-  /stream-browserify@2.0.2:
-    resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==}
-    dependencies:
-      inherits: 2.0.4
-      readable-stream: 2.3.8
-    dev: true
-
-  /stream-each@1.2.3:
-    resolution: {integrity: sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==}
-    dependencies:
-      end-of-stream: 1.4.4
-      stream-shift: 1.0.1
-    dev: true
-
-  /stream-http@2.8.3:
-    resolution: {integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==}
-    dependencies:
-      builtin-status-codes: 3.0.0
-      inherits: 2.0.4
-      readable-stream: 2.3.8
-      to-arraybuffer: 1.0.1
-      xtend: 4.0.2
-    dev: true
-
-  /stream-shift@1.0.1:
-    resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==}
-    dev: true
-
-  /streamroller@3.1.5:
-    resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==}
-    engines: {node: '>=8.0'}
-    dependencies:
-      date-format: 4.0.14
-      debug: 4.3.4(supports-color@9.4.0)
-      fs-extra: 8.1.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /string-argv@0.3.2:
-    resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
-    engines: {node: '>=0.6.19'}
-    dev: true
-
-  /string-hash@1.1.3:
-    resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==}
-    dev: true
-
-  /string-width@4.2.3:
-    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
-    engines: {node: '>=8'}
-    dependencies:
-      emoji-regex: 8.0.0
-      is-fullwidth-code-point: 3.0.0
-      strip-ansi: 6.0.1
-    dev: true
-
-  /string-width@5.1.2:
-    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
-    engines: {node: '>=12'}
-    dependencies:
-      eastasianwidth: 0.2.0
-      emoji-regex: 9.2.2
-      strip-ansi: 7.1.0
-    dev: true
-
-  /string_decoder@1.1.1:
-    resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
-    dependencies:
-      safe-buffer: 5.1.2
-    dev: true
-
-  /string_decoder@1.3.0:
-    resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
-    dependencies:
-      safe-buffer: 5.2.1
-    dev: true
-
-  /strip-ansi@6.0.1:
-    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
-    engines: {node: '>=8'}
-    dependencies:
-      ansi-regex: 5.0.1
-    dev: true
-
-  /strip-ansi@7.1.0:
-    resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
-    engines: {node: '>=12'}
-    dependencies:
-      ansi-regex: 6.0.1
-    dev: true
-
-  /strip-bom@3.0.0:
-    resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /strip-eof@1.0.0:
-    resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /strip-final-newline@2.0.0:
-    resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /strip-final-newline@3.0.0:
-    resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /strip-indent@2.0.0:
-    resolution: {integrity: sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /strip-indent@3.0.0:
-    resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      min-indent: 1.0.1
-    dev: true
-
-  /strip-json-comments@3.1.1:
-    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /strip-literal@1.3.0:
-    resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==}
-    dependencies:
-      acorn: 8.11.2
-    dev: true
-
-  /stylus@0.58.1:
-    resolution: {integrity: sha512-AYiCHm5ogczdCPMfe9aeQa4NklB2gcf4D/IhzYPddJjTgPc+k4D/EVE0yfQbZD43MHP3lPy+8NZ9fcFxkrgs/w==}
-    hasBin: true
-    dependencies:
-      css: 3.0.0
-      debug: 4.3.4(supports-color@9.4.0)
-      glob: 7.2.3
-      sax: 1.2.4
-      source-map: 0.7.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /supports-color@5.5.0:
-    resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
-    engines: {node: '>=4'}
-    dependencies:
-      has-flag: 3.0.0
-    dev: true
-
-  /supports-color@7.2.0:
-    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
-    engines: {node: '>=8'}
-    dependencies:
-      has-flag: 4.0.0
-
-  /supports-color@9.4.0:
-    resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /supports-preserve-symlinks-flag@1.0.0:
-    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
-    engines: {node: '>= 0.4'}
-
-  /symbol-tree@3.2.4:
-    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
-    dev: true
-
-  /tapable@1.1.3:
-    resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /tar-fs@2.1.1:
-    resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
-    dependencies:
-      chownr: 1.1.4
-      mkdirp-classic: 0.5.3
-      pump: 3.0.0
-      tar-stream: 2.2.0
-    dev: true
-
-  /tar-stream@2.2.0:
-    resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
-    engines: {node: '>=6'}
-    dependencies:
-      bl: 4.1.0
-      end-of-stream: 1.4.4
-      fs-constants: 1.0.0
-      inherits: 2.0.4
-      readable-stream: 3.6.2
-    dev: true
-
-  /temp-dir@2.0.0:
-    resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /tempfile@3.0.0:
-    resolution: {integrity: sha512-uNFCg478XovRi85iD42egu+eSFUmmka750Jy7L5tfHI5hQKKtbPnxaSaXAbBqCDYrw3wx4tXjKwci4/QmsZJxw==}
-    engines: {node: '>=8'}
-    dependencies:
-      temp-dir: 2.0.0
-      uuid: 3.4.0
-    dev: true
-
-  /terser-webpack-plugin@1.4.5(webpack@4.47.0):
-    resolution: {integrity: sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==}
-    engines: {node: '>= 6.9.0'}
-    peerDependencies:
-      webpack: ^4.0.0
-    dependencies:
-      cacache: 12.0.4
-      find-cache-dir: 2.1.0
-      is-wsl: 1.1.0
-      schema-utils: 1.0.0
-      serialize-javascript: 4.0.0
-      source-map: 0.6.1
-      terser: 4.8.1
-      webpack: 4.47.0
-      webpack-sources: 1.4.3
-      worker-farm: 1.7.0
-    dev: true
-
-  /terser@4.8.1:
-    resolution: {integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-    dependencies:
-      acorn: 8.11.2
-      commander: 2.20.3
-      source-map: 0.6.1
-      source-map-support: 0.5.21
-    dev: true
-
-  /terser@5.25.0:
-    resolution: {integrity: sha512-we0I9SIsfvNUMP77zC9HG+MylwYYsGFSBG8qm+13oud2Yh+O104y614FRbyjpxys16jZwot72Fpi827YvGzuqg==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      '@jridgewell/source-map': 0.3.5
-      acorn: 8.11.2
-      commander: 2.20.3
-      source-map-support: 0.5.21
-    dev: true
-
-  /text-extensions@1.9.0:
-    resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==}
-    engines: {node: '>=0.10'}
-    dev: true
-
-  /through2@2.0.5:
-    resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
-    dependencies:
-      readable-stream: 2.3.8
-      xtend: 4.0.2
-    dev: true
-
-  /through2@4.0.2:
-    resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
-    dependencies:
-      readable-stream: 3.6.2
-    dev: true
-
-  /through@2.3.8:
-    resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
-    dev: true
-
-  /timers-browserify@2.0.12:
-    resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==}
-    engines: {node: '>=0.6.0'}
-    dependencies:
-      setimmediate: 1.0.5
-    dev: true
-
-  /tinybench@2.5.1:
-    resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==}
-    dev: true
-
-  /tinypool@0.8.1:
-    resolution: {integrity: sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==}
-    engines: {node: '>=14.0.0'}
-    dev: true
-
-  /tinyspy@2.2.0:
-    resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==}
-    engines: {node: '>=14.0.0'}
-    dev: true
-
-  /tmp@0.2.1:
-    resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==}
-    engines: {node: '>=8.17.0'}
-    dependencies:
-      rimraf: 3.0.2
-    dev: true
-
-  /to-arraybuffer@1.0.1:
-    resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==}
-    dev: true
-
-  /to-fast-properties@2.0.0:
-    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
-    engines: {node: '>=4'}
-
-  /to-object-path@0.3.0:
-    resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      kind-of: 3.2.2
-    dev: true
-
-  /to-regex-range@2.1.1:
-    resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-number: 3.0.0
-      repeat-string: 1.6.1
-    dev: true
-
-  /to-regex-range@5.0.1:
-    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
-    engines: {node: '>=8.0'}
-    dependencies:
-      is-number: 7.0.0
-    dev: true
-
-  /to-regex@3.0.2:
-    resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      define-property: 2.0.2
-      extend-shallow: 3.0.2
-      regex-not: 1.0.2
-      safe-regex: 1.1.0
-    dev: true
-
-  /todomvc-app-css@2.4.3:
-    resolution: {integrity: sha512-mSnWZaKBWj9aQcFRsGguY/a8O8NR8GmecD48yU1rzwNemgZa/INLpIsxxMiToFGVth+uEKBrQ7IhWkaXZxwq5Q==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /toidentifier@1.0.1:
-    resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
-    engines: {node: '>=0.6'}
-    dev: true
-
-  /token-stream@1.0.0:
-    resolution: {integrity: sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==}
-    dev: true
-
-  /tough-cookie@4.1.3:
-    resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
-    engines: {node: '>=6'}
-    dependencies:
-      psl: 1.9.0
-      punycode: 2.3.1
-      universalify: 0.2.0
-      url-parse: 1.5.10
-    dev: true
-
-  /tr46@0.0.3:
-    resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
-    dev: true
-
-  /tr46@3.0.0:
-    resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==}
-    engines: {node: '>=12'}
-    dependencies:
-      punycode: 2.3.1
-    dev: true
-
-  /trim-newlines@3.0.1:
-    resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /ts-node@10.9.1(@types/node@20.10.3)(typescript@4.9.5):
-    resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
-    hasBin: true
-    peerDependencies:
-      '@swc/core': '>=1.2.50'
-      '@swc/wasm': '>=1.2.50'
-      '@types/node': '*'
-      typescript: '>=2.7'
-    peerDependenciesMeta:
-      '@swc/core':
-        optional: true
-      '@swc/wasm':
-        optional: true
-    dependencies:
-      '@cspotcode/source-map-support': 0.8.1
-      '@tsconfig/node10': 1.0.9
-      '@tsconfig/node12': 1.0.11
-      '@tsconfig/node14': 1.0.3
-      '@tsconfig/node16': 1.0.4
-      '@types/node': 20.10.3
-      acorn: 8.11.2
-      acorn-walk: 8.3.1
-      arg: 4.1.3
-      create-require: 1.1.1
-      diff: 4.0.2
-      make-error: 1.3.6
-      typescript: 4.9.5
-      v8-compile-cache-lib: 3.0.1
-      yn: 3.1.1
-    dev: true
-
-  /tslib@2.6.2:
-    resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
-    dev: true
-
-  /tty-browserify@0.0.0:
-    resolution: {integrity: sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==}
-    dev: true
-
-  /type-detect@4.0.8:
-    resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /type-fest@0.18.1:
-    resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /type-fest@0.21.3:
-    resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /type-fest@0.6.0:
-    resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /type-fest@0.8.1:
-    resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /type-is@1.6.18:
-    resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
-    engines: {node: '>= 0.6'}
-    dependencies:
-      media-typer: 0.3.0
-      mime-types: 2.1.35
-    dev: true
-
-  /typedarray@0.0.6:
-    resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
-    dev: true
-
-  /typescript@4.9.5:
-    resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
-    engines: {node: '>=4.2.0'}
-    hasBin: true
-    dev: true
-
-  /typescript@5.0.4:
-    resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==}
-    engines: {node: '>=12.20'}
-    hasBin: true
-    dev: true
-
-  /ua-parser-js@0.7.37:
-    resolution: {integrity: sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA==}
-    dev: true
-
-  /ufo@1.3.2:
-    resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==}
-    dev: true
-
-  /uglify-js@3.17.4:
-    resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==}
-    engines: {node: '>=0.8.0'}
-    hasBin: true
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /unbzip2-stream@1.4.3:
-    resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==}
-    dependencies:
-      buffer: 5.7.1
-      through: 2.3.8
-    dev: true
-
-  /undici-types@5.26.5:
-    resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
-    dev: true
-
-  /union-value@1.0.1:
-    resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-union: 3.1.0
-      get-value: 2.0.6
-      is-extendable: 0.1.1
-      set-value: 2.0.1
-    dev: true
-
-  /unique-filename@1.1.1:
-    resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==}
-    dependencies:
-      unique-slug: 2.0.2
-    dev: true
-
-  /unique-slug@2.0.2:
-    resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==}
-    dependencies:
-      imurmurhash: 0.1.4
-    dev: true
-
-  /universalify@0.1.2:
-    resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
-    engines: {node: '>= 4.0.0'}
-    dev: true
-
-  /universalify@0.2.0:
-    resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
-    engines: {node: '>= 4.0.0'}
-    dev: true
-
-  /universalify@2.0.1:
-    resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
-    engines: {node: '>= 10.0.0'}
-    dev: true
-
-  /unpipe@1.0.0:
-    resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
-    engines: {node: '>= 0.8'}
-    dev: true
-
-  /unset-value@1.0.0:
-    resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      has-value: 0.3.1
-      isobject: 3.0.1
-    dev: true
-
-  /upath@1.2.0:
-    resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==}
-    engines: {node: '>=4'}
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /uri-js@4.4.1:
-    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
-    dependencies:
-      punycode: 2.3.1
-    dev: true
-
-  /urix@0.1.0:
-    resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==}
-    deprecated: Please see https://github.com/lydell/urix#deprecated
-    dev: true
-
-  /url-parse@1.5.10:
-    resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
-    dependencies:
-      querystringify: 2.2.0
-      requires-port: 1.0.0
-    dev: true
-
-  /url@0.11.3:
-    resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==}
-    dependencies:
-      punycode: 1.4.1
-      qs: 6.11.2
-    dev: true
-
-  /use@3.1.1:
-    resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /util-deprecate@1.0.2:
-    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
-    dev: true
-
-  /util@0.10.4:
-    resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==}
-    dependencies:
-      inherits: 2.0.3
-    dev: true
-
-  /util@0.11.1:
-    resolution: {integrity: sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==}
-    dependencies:
-      inherits: 2.0.3
-    dev: true
-
-  /utils-merge@1.0.1:
-    resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
-    engines: {node: '>= 0.4.0'}
-    dev: true
-
-  /uuid@3.4.0:
-    resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
-    deprecated: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
-    hasBin: true
-    dev: true
-
-  /v8-compile-cache-lib@3.0.1:
-    resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
-    dev: true
-
-  /validate-npm-package-license@3.0.4:
-    resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
-    dependencies:
-      spdx-correct: 3.2.0
-      spdx-expression-parse: 3.0.1
-    dev: true
-
-  /validator@13.11.0:
-    resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==}
-    engines: {node: '>= 0.10'}
-    dev: true
-
-  /vary@1.1.2:
-    resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
-    engines: {node: '>= 0.8'}
-    dev: true
-
-  /vite-node@1.0.4(@types/node@20.10.3)(terser@5.25.0):
-    resolution: {integrity: sha512-9xQQtHdsz5Qn8hqbV7UKqkm8YkJhzT/zr41Dmt5N7AlD8hJXw/Z7y0QiD5I8lnTthV9Rvcvi0QW7PI0Fq83ZPg==}
-    engines: {node: ^18.0.0 || >=20.0.0}
-    hasBin: true
-    dependencies:
-      cac: 6.7.14
-      debug: 4.3.4(supports-color@9.4.0)
-      pathe: 1.1.1
-      picocolors: 1.0.0
-      vite: 5.0.5(@types/node@20.10.3)(terser@5.25.0)
-    transitivePeerDependencies:
-      - '@types/node'
-      - less
-      - lightningcss
-      - sass
-      - stylus
-      - sugarss
-      - supports-color
-      - terser
-    dev: true
-
-  /vite@5.0.5(@types/node@20.10.3)(terser@5.25.0):
-    resolution: {integrity: sha512-OekeWqR9Ls56f3zd4CaxzbbS11gqYkEiBtnWFFgYR2WV8oPJRRKq0mpskYy/XaoCL3L7VINDhqqOMNDiYdGvGg==}
-    engines: {node: ^18.0.0 || >=20.0.0}
-    hasBin: true
-    peerDependencies:
-      '@types/node': ^18.0.0 || >=20.0.0
-      less: '*'
-      lightningcss: ^1.21.0
-      sass: '*'
-      stylus: '*'
-      sugarss: '*'
-      terser: ^5.4.0
-    peerDependenciesMeta:
-      '@types/node':
-        optional: true
-      less:
-        optional: true
-      lightningcss:
-        optional: true
-      sass:
-        optional: true
-      stylus:
-        optional: true
-      sugarss:
-        optional: true
-      terser:
-        optional: true
-    dependencies:
-      '@types/node': 20.10.3
-      esbuild: 0.19.8
-      postcss: 8.4.32
-      rollup: 4.6.1
-      terser: 5.25.0
-    optionalDependencies:
-      fsevents: 2.3.3
-    dev: true
-
-  /vitest@1.0.4(@types/node@20.10.3)(jsdom@19.0.0)(terser@5.25.0):
-    resolution: {integrity: sha512-s1GQHp/UOeWEo4+aXDOeFBJwFzL6mjycbQwwKWX2QcYfh/7tIerS59hWQ20mxzupTJluA2SdwiBuWwQHH67ckg==}
-    engines: {node: ^18.0.0 || >=20.0.0}
-    hasBin: true
-    peerDependencies:
-      '@edge-runtime/vm': '*'
-      '@types/node': ^18.0.0 || >=20.0.0
-      '@vitest/browser': ^1.0.0
-      '@vitest/ui': ^1.0.0
-      happy-dom: '*'
-      jsdom: '*'
-    peerDependenciesMeta:
-      '@edge-runtime/vm':
-        optional: true
-      '@types/node':
-        optional: true
-      '@vitest/browser':
-        optional: true
-      '@vitest/ui':
-        optional: true
-      happy-dom:
-        optional: true
-      jsdom:
-        optional: true
-    dependencies:
-      '@types/node': 20.10.3
-      '@vitest/expect': 1.0.4
-      '@vitest/runner': 1.0.4
-      '@vitest/snapshot': 1.0.4
-      '@vitest/spy': 1.0.4
-      '@vitest/utils': 1.0.4
-      acorn-walk: 8.3.1
-      cac: 6.7.14
-      chai: 4.3.10
-      debug: 4.3.4(supports-color@9.4.0)
-      execa: 8.0.1
-      jsdom: 19.0.0
-      local-pkg: 0.5.0
-      magic-string: 0.30.5
-      pathe: 1.1.1
-      picocolors: 1.0.0
-      std-env: 3.6.0
-      strip-literal: 1.3.0
-      tinybench: 2.5.1
-      tinypool: 0.8.1
-      vite: 5.0.5(@types/node@20.10.3)(terser@5.25.0)
-      vite-node: 1.0.4(@types/node@20.10.3)(terser@5.25.0)
-      why-is-node-running: 2.2.2
-    transitivePeerDependencies:
-      - less
-      - lightningcss
-      - sass
-      - stylus
-      - sugarss
-      - supports-color
-      - terser
-    dev: true
-
-  /vm-browserify@1.1.2:
-    resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==}
-    dev: true
-
-  /void-elements@2.0.1:
-    resolution: {integrity: sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /void-elements@3.1.0:
-    resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /w3c-hr-time@1.0.2:
-    resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
-    deprecated: Use your platform's native performance.now() and performance.timeOrigin.
-    dependencies:
-      browser-process-hrtime: 1.0.0
-    dev: true
-
-  /w3c-xmlserializer@3.0.0:
-    resolution: {integrity: sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==}
-    engines: {node: '>=12'}
-    dependencies:
-      xml-name-validator: 4.0.0
-    dev: true
-
-  /watchpack-chokidar2@2.0.1:
-    resolution: {integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==}
-    requiresBuild: true
-    dependencies:
-      chokidar: 2.1.8
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-    optional: true
-
-  /watchpack@1.7.5:
-    resolution: {integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==}
-    dependencies:
-      graceful-fs: 4.2.11
-      neo-async: 2.6.2
-    optionalDependencies:
-      chokidar: 3.5.3
-      watchpack-chokidar2: 2.0.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /webidl-conversions@3.0.1:
-    resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
-    dev: true
-
-  /webidl-conversions@7.0.0:
-    resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /webpack-sources@1.4.3:
-    resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==}
-    dependencies:
-      source-list-map: 2.0.1
-      source-map: 0.6.1
-    dev: true
-
-  /webpack@4.47.0:
-    resolution: {integrity: sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==}
-    engines: {node: '>=6.11.5'}
-    hasBin: true
-    peerDependencies:
-      webpack-cli: '*'
-      webpack-command: '*'
-    peerDependenciesMeta:
-      webpack-cli:
-        optional: true
-      webpack-command:
-        optional: true
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-module-context': 1.9.0
-      '@webassemblyjs/wasm-edit': 1.9.0
-      '@webassemblyjs/wasm-parser': 1.9.0
-      acorn: 6.4.2
-      ajv: 6.12.6
-      ajv-keywords: 3.5.2(ajv@6.12.6)
-      chrome-trace-event: 1.0.3
-      enhanced-resolve: 4.5.0
-      eslint-scope: 4.0.3
-      json-parse-better-errors: 1.0.2
-      loader-runner: 2.4.0
-      loader-utils: 1.4.2
-      memory-fs: 0.4.1
-      micromatch: 3.1.10
-      mkdirp: 0.5.6
-      neo-async: 2.6.2
-      node-libs-browser: 2.2.1
-      schema-utils: 1.0.0
-      tapable: 1.1.3
-      terser-webpack-plugin: 1.4.5(webpack@4.47.0)
-      watchpack: 1.7.5
-      webpack-sources: 1.4.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /whatwg-encoding@2.0.0:
-    resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
-    engines: {node: '>=12'}
-    dependencies:
-      iconv-lite: 0.6.3
-    dev: true
-
-  /whatwg-mimetype@3.0.0:
-    resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /whatwg-url@10.0.0:
-    resolution: {integrity: sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==}
-    engines: {node: '>=12'}
-    dependencies:
-      tr46: 3.0.0
-      webidl-conversions: 7.0.0
-    dev: true
-
-  /whatwg-url@11.0.0:
-    resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==}
-    engines: {node: '>=12'}
-    dependencies:
-      tr46: 3.0.0
-      webidl-conversions: 7.0.0
-    dev: true
-
-  /whatwg-url@5.0.0:
-    resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
-    dependencies:
-      tr46: 0.0.3
-      webidl-conversions: 3.0.1
-    dev: true
-
-  /which@1.3.1:
-    resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
-    hasBin: true
-    dependencies:
-      isexe: 2.0.0
-    dev: true
-
-  /which@2.0.2:
-    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
-    engines: {node: '>= 8'}
-    hasBin: true
-    dependencies:
-      isexe: 2.0.0
-    dev: true
-
-  /why-is-node-running@2.2.2:
-    resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==}
-    engines: {node: '>=8'}
-    hasBin: true
-    dependencies:
-      siginfo: 2.0.0
-      stackback: 0.0.2
-    dev: true
-
-  /with@7.0.2:
-    resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
-    engines: {node: '>= 10.0.0'}
-    dependencies:
-      '@babel/parser': 7.23.5
-      '@babel/types': 7.23.5
-      assert-never: 1.2.1
-      babel-walk: 3.0.0-canary-5
-    dev: true
-
-  /wordwrap@1.0.0:
-    resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
-    dev: true
-
-  /worker-farm@1.7.0:
-    resolution: {integrity: sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==}
-    dependencies:
-      errno: 0.1.8
-    dev: true
-
-  /wrap-ansi@6.2.0:
-    resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
-    engines: {node: '>=8'}
-    dependencies:
-      ansi-styles: 4.3.0
-      string-width: 4.2.3
-      strip-ansi: 6.0.1
-    dev: true
-
-  /wrap-ansi@7.0.0:
-    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
-    engines: {node: '>=10'}
-    dependencies:
-      ansi-styles: 4.3.0
-      string-width: 4.2.3
-      strip-ansi: 6.0.1
-    dev: true
-
-  /wrappy@1.0.2:
-    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
-    dev: true
-
-  /ws@8.11.0:
-    resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==}
-    engines: {node: '>=10.0.0'}
-    peerDependencies:
-      bufferutil: ^4.0.1
-      utf-8-validate: ^5.0.2
-    peerDependenciesMeta:
-      bufferutil:
-        optional: true
-      utf-8-validate:
-        optional: true
-    dev: true
-
-  /ws@8.14.2:
-    resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==}
-    engines: {node: '>=10.0.0'}
-    peerDependencies:
-      bufferutil: ^4.0.1
-      utf-8-validate: '>=5.0.2'
-    peerDependenciesMeta:
-      bufferutil:
-        optional: true
-      utf-8-validate:
-        optional: true
-    dev: true
-
-  /ws@8.7.0:
-    resolution: {integrity: sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==}
-    engines: {node: '>=10.0.0'}
-    peerDependencies:
-      bufferutil: ^4.0.1
-      utf-8-validate: ^5.0.2
-    peerDependenciesMeta:
-      bufferutil:
-        optional: true
-      utf-8-validate:
-        optional: true
-    dev: true
-
-  /xml-name-validator@4.0.0:
-    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
-    engines: {node: '>=12'}
-    dev: true
-
-  /xmlchars@2.2.0:
-    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
-    dev: true
-
-  /xtend@4.0.2:
-    resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
-    engines: {node: '>=0.4'}
-    dev: true
-
-  /y18n@4.0.3:
-    resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
-    dev: true
-
-  /y18n@5.0.8:
-    resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /yallist@2.1.2:
-    resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
-    dev: true
-
-  /yallist@3.1.1:
-    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
-    dev: true
-
-  /yallist@4.0.0:
-    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
-    dev: true
-
-  /yaml@1.10.2:
-    resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
-    engines: {node: '>= 6'}
-    dev: true
-
-  /yargs-parser@20.2.9:
-    resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /yargs@16.2.0:
-    resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
-    engines: {node: '>=10'}
-    dependencies:
-      cliui: 7.0.4
-      escalade: 3.1.1
-      get-caller-file: 2.0.5
-      require-directory: 2.1.1
-      string-width: 4.2.3
-      y18n: 5.0.8
-      yargs-parser: 20.2.9
-    dev: true
-
-  /yauzl@2.10.0:
-    resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
-    dependencies:
-      buffer-crc32: 0.2.13
-      fd-slicer: 1.1.0
-    dev: true
-
-  /yn@3.1.1:
-    resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /yocto-queue@1.0.0:
-    resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
-    engines: {node: '>=12.20'}
-    dev: true
-
-  /yorkie@2.0.0:
-    resolution: {integrity: sha512-jcKpkthap6x63MB4TxwCyuIGkV0oYP/YRyuQU5UO0Yz/E/ZAu+653/uov+phdmO54n6BcvFRyyt0RRrWdN2mpw==}
-    engines: {node: '>=4'}
-    requiresBuild: true
-    dependencies:
-      execa: 0.8.0
-      is-ci: 1.2.1
-      normalize-path: 1.0.0
-      strip-indent: 2.0.0
-    dev: true
-
-  /z-schema@5.0.5:
-    resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==}
-    engines: {node: '>=8.0.0'}
-    hasBin: true
-    dependencies:
-      lodash.get: 4.4.2
-      lodash.isequal: 4.5.0
-      validator: 13.11.0
-    optionalDependencies:
-      commander: 9.5.0
-    dev: true
-
-  'file:':
-    resolution: {directory: '', type: directory}
-    name: vue
-    dependencies:
-      '@vue/compiler-sfc': link:packages/compiler-sfc
-      csstype: 3.1.2
-    dev: true
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
deleted file mode 100644
index 18ec407efca..00000000000
--- a/pnpm-workspace.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-packages:
-  - 'packages/*'
diff --git a/scripts/alias.js b/scripts/alias.js
deleted file mode 100644
index 874686da40d..00000000000
--- a/scripts/alias.js
+++ /dev/null
@@ -1,13 +0,0 @@
-const path = require('path')
-
-const resolve = p => path.resolve(__dirname, '../', p)
-
-module.exports = {
-  vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
-  compiler: resolve('src/compiler'),
-  core: resolve('src/core'),
-  shared: resolve('src/shared'),
-  web: resolve('src/platforms/web'),
-  server: resolve('packages/server-renderer/src'),
-  sfc: resolve('packages/compiler-sfc/src')
-}
diff --git a/scripts/build.js b/scripts/build.js
deleted file mode 100644
index 2468512af4c..00000000000
--- a/scripts/build.js
+++ /dev/null
@@ -1,97 +0,0 @@
-const fs = require('fs')
-const path = require('path')
-const zlib = require('zlib')
-const rollup = require('rollup')
-const terser = require('terser')
-
-if (!fs.existsSync('dist')) {
-  fs.mkdirSync('dist')
-}
-
-let builds = require('./config').getAllBuilds()
-
-// filter builds via command line arg
-if (process.argv[2]) {
-  const filters = process.argv[2].split(',')
-  builds = builds.filter(b => {
-    return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)
-  })
-}
-
-build(builds)
-
-function build (builds) {
-  let built = 0
-  const total = builds.length
-  const next = () => {
-    buildEntry(builds[built]).then(() => {
-      built++
-      if (built < total) {
-        next()
-      }
-    }).catch(logError)
-  }
-
-  next()
-}
-
-function buildEntry (config) {
-  const output = config.output
-  const { file, banner } = output
-  const isProd = /(min|prod)\.js$/.test(file)
-  return rollup.rollup(config)
-    .then(bundle => bundle.generate(output))
-    .then(async ({ output: [{ code }] }) => {
-      if (isProd) {
-        const {code: minifiedCode} =  await terser.minify(code, {
-          toplevel: true,
-          compress: {
-            pure_funcs: ['makeMap'],
-          },
-          format: {
-            ascii_only: true,
-          }
-        });
-        const minified = (banner ? banner + '\n' : '') + minifiedCode
-        return write(file, minified, true)
-      } else {
-        return write(file, code)
-      }
-    })
-}
-
-function write (dest, code, zip) {
-  return new Promise((resolve, reject) => {
-    function report (extra) {
-      console.log(blue(path.relative(process.cwd(), dest)) + ' ' + getSize(code) + (extra || ''))
-      resolve()
-    }
-
-    if (!fs.existsSync(path.dirname(dest))) {
-      fs.mkdirSync(path.dirname(dest), { recursive: true })
-    }
-    fs.writeFile(dest, code, err => {
-      if (err) return reject(err)
-      if (zip) {
-        zlib.gzip(code, (err, zipped) => {
-          if (err) return reject(err)
-          report(' (gzipped: ' + getSize(zipped) + ')')
-        })
-      } else {
-        report()
-      }
-    })
-  })
-}
-
-function getSize (code) {
-  return (code.length / 1024).toFixed(2) + 'kb'
-}
-
-function logError (e) {
-  console.log(e)
-}
-
-function blue (str) {
-  return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m'
-}
diff --git a/scripts/config.js b/scripts/config.js
deleted file mode 100644
index e9f519853f1..00000000000
--- a/scripts/config.js
+++ /dev/null
@@ -1,305 +0,0 @@
-const path = require('path')
-const alias = require('@rollup/plugin-alias')
-const cjs = require('@rollup/plugin-commonjs')
-const replace = require('@rollup/plugin-replace')
-const node = require('@rollup/plugin-node-resolve').nodeResolve
-const ts = require('rollup-plugin-typescript2')
-
-const version = process.env.VERSION || require('../package.json').version
-const featureFlags = require('./feature-flags')
-
-const banner =
-  '/*!\n' +
-  ` * Vue.js v${version}\n` +
-  ` * (c) 2014-${new Date().getFullYear()} Evan You\n` +
-  ' * Released under the MIT License.\n' +
-  ' */'
-
-const aliases = require('./alias')
-const resolve = p => {
-  const base = p.split('/')[0]
-  if (aliases[base]) {
-    return path.resolve(aliases[base], p.slice(base.length + 1))
-  } else {
-    return path.resolve(__dirname, '../', p)
-  }
-}
-
-// we are bundling forked consolidate.js in compiler-sfc which dynamically
-// requires a ton of template engines which should be ignored.
-const consolidatePath = require.resolve('@vue/consolidate/package.json', {
-  paths: [path.resolve(__dirname, '../packages/compiler-sfc')]
-})
-
-const builds = {
-  // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
-  'runtime-cjs-dev': {
-    entry: resolve('web/entry-runtime.ts'),
-    dest: resolve('dist/vue.runtime.common.dev.js'),
-    format: 'cjs',
-    env: 'development',
-    banner
-  },
-  'runtime-cjs-prod': {
-    entry: resolve('web/entry-runtime.ts'),
-    dest: resolve('dist/vue.runtime.common.prod.js'),
-    format: 'cjs',
-    env: 'production',
-    banner
-  },
-  // Runtime+compiler CommonJS build (CommonJS)
-  'full-cjs-dev': {
-    entry: resolve('web/entry-runtime-with-compiler.ts'),
-    dest: resolve('dist/vue.common.dev.js'),
-    format: 'cjs',
-    env: 'development',
-    alias: { he: './entity-decoder' },
-    banner
-  },
-  'full-cjs-prod': {
-    entry: resolve('web/entry-runtime-with-compiler.ts'),
-    dest: resolve('dist/vue.common.prod.js'),
-    format: 'cjs',
-    env: 'production',
-    alias: { he: './entity-decoder' },
-    banner
-  },
-  // Runtime only ES modules build (for bundlers)
-  'runtime-esm': {
-    entry: resolve('web/entry-runtime-esm.ts'),
-    dest: resolve('dist/vue.runtime.esm.js'),
-    format: 'es',
-    banner
-  },
-  // Runtime+compiler ES modules build (for bundlers)
-  'full-esm': {
-    entry: resolve('web/entry-runtime-with-compiler-esm.ts'),
-    dest: resolve('dist/vue.esm.js'),
-    format: 'es',
-    alias: { he: './entity-decoder' },
-    banner
-  },
-  // Runtime+compiler ES modules build (for direct import in browser)
-  'full-esm-browser-dev': {
-    entry: resolve('web/entry-runtime-with-compiler-esm.ts'),
-    dest: resolve('dist/vue.esm.browser.js'),
-    format: 'es',
-    transpile: false,
-    env: 'development',
-    alias: { he: './entity-decoder' },
-    banner
-  },
-  // Runtime+compiler ES modules build (for direct import in browser)
-  'full-esm-browser-prod': {
-    entry: resolve('web/entry-runtime-with-compiler-esm.ts'),
-    dest: resolve('dist/vue.esm.browser.min.js'),
-    format: 'es',
-    transpile: false,
-    env: 'production',
-    alias: { he: './entity-decoder' },
-    banner
-  },
-  // runtime-only build (Browser)
-  'runtime-dev': {
-    entry: resolve('web/entry-runtime.ts'),
-    dest: resolve('dist/vue.runtime.js'),
-    format: 'umd',
-    env: 'development',
-    banner
-  },
-  // runtime-only production build (Browser)
-  'runtime-prod': {
-    entry: resolve('web/entry-runtime.ts'),
-    dest: resolve('dist/vue.runtime.min.js'),
-    format: 'umd',
-    env: 'production',
-    banner
-  },
-  // Runtime+compiler development build (Browser)
-  'full-dev': {
-    entry: resolve('web/entry-runtime-with-compiler.ts'),
-    dest: resolve('dist/vue.js'),
-    format: 'umd',
-    env: 'development',
-    alias: { he: './entity-decoder' },
-    banner
-  },
-  // Runtime+compiler production build  (Browser)
-  'full-prod': {
-    entry: resolve('web/entry-runtime-with-compiler.ts'),
-    dest: resolve('dist/vue.min.js'),
-    format: 'umd',
-    env: 'production',
-    alias: { he: './entity-decoder' },
-    banner
-  },
-  // Web compiler (CommonJS).
-  compiler: {
-    entry: resolve('web/entry-compiler.ts'),
-    dest: resolve('packages/template-compiler/build.js'),
-    format: 'cjs',
-    external: Object.keys(
-      require('../packages/template-compiler/package.json').dependencies
-    )
-  },
-  // Web compiler (UMD for in-browser use).
-  'compiler-browser': {
-    entry: resolve('web/entry-compiler.ts'),
-    dest: resolve('packages/template-compiler/browser.js'),
-    format: 'umd',
-    env: 'development',
-    moduleName: 'VueTemplateCompiler',
-    plugins: [node(), cjs()]
-  },
-  // Web server renderer (CommonJS).
-  'server-renderer-dev': {
-    entry: resolve('packages/server-renderer/src/index.ts'),
-    dest: resolve('packages/server-renderer/build.dev.js'),
-    format: 'cjs',
-    env: 'development',
-    external: [
-      'stream',
-      ...Object.keys(
-        require('../packages/server-renderer/package.json').dependencies
-      )
-    ]
-  },
-  'server-renderer-prod': {
-    entry: resolve('server/index.ts'),
-    dest: resolve('packages/server-renderer/build.prod.js'),
-    format: 'cjs',
-    env: 'production',
-    external: [
-      'stream',
-      ...Object.keys(
-        require('../packages/server-renderer/package.json').dependencies
-      )
-    ]
-  },
-  'server-renderer-basic': {
-    entry: resolve('server/index-basic.ts'),
-    dest: resolve('packages/server-renderer/basic.js'),
-    format: 'umd',
-    env: 'development',
-    moduleName: 'renderVueComponentToString',
-    plugins: [node(), cjs()]
-  },
-  'server-renderer-webpack-server-plugin': {
-    entry: resolve('server/webpack-plugin/server.ts'),
-    dest: resolve('packages/server-renderer/server-plugin.js'),
-    format: 'cjs',
-    external: Object.keys(
-      require('../packages/server-renderer/package.json').dependencies
-    )
-  },
-  'server-renderer-webpack-client-plugin': {
-    entry: resolve('server/webpack-plugin/client.ts'),
-    dest: resolve('packages/server-renderer/client-plugin.js'),
-    format: 'cjs',
-    external: Object.keys(
-      require('../packages/server-renderer/package.json').dependencies
-    )
-  },
-  'compiler-sfc': {
-    entry: resolve('packages/compiler-sfc/src/index.ts'),
-    dest: resolve('packages/compiler-sfc/dist/compiler-sfc.js'),
-    format: 'cjs',
-    external: Object.keys(
-      require('../packages/compiler-sfc/package.json').dependencies
-    ),
-    plugins: [
-      node({ preferBuiltins: true }),
-      cjs({
-        ignore: [
-          ...Object.keys(require(consolidatePath).devDependencies),
-          'vm',
-          'crypto',
-          'react-dom/server',
-          'teacup/lib/express',
-          'arc-templates/dist/es5',
-          'then-pug',
-          'then-jade'
-        ]
-      })
-    ]
-  }
-}
-
-function genConfig(name) {
-  const opts = builds[name]
-  const isTargetingBrowser = !(
-    opts.transpile === false || opts.format === 'cjs'
-  )
-
-  // console.log('__dir', __dirname)
-  const config = {
-    input: opts.entry,
-    external: opts.external,
-    plugins: [
-      alias({
-        entries: Object.assign({}, aliases, opts.alias)
-      }),
-      ts({
-        tsconfig: path.resolve(__dirname, '../', 'tsconfig.json'),
-        cacheRoot: path.resolve(__dirname, '../', 'node_modules/.rts2_cache'),
-        tsconfigOverride: {
-          compilerOptions: {
-            // if targeting browser, target es5
-            // if targeting node, es2017 means Node 8
-            target: isTargetingBrowser ? 'es5' : 'es2017'
-          },
-          include: isTargetingBrowser ? ['src'] : ['src', 'packages/*/src'],
-          exclude: ['test', 'test-dts']
-        }
-      })
-    ].concat(opts.plugins || []),
-    output: {
-      file: opts.dest,
-      format: opts.format,
-      banner: opts.banner,
-      name: opts.moduleName || 'Vue',
-      exports: 'auto'
-    },
-    onwarn: (msg, warn) => {
-      if (!/Circular/.test(msg)) {
-        warn(msg)
-      }
-    }
-  }
-
-  // console.log('pluging', config.plugins)
-
-  // built-in vars
-  const vars = {
-    __VERSION__: version,
-    __DEV__: `process.env.NODE_ENV !== 'production'`,
-    __TEST__: false,
-    __GLOBAL__: opts.format === 'umd' || name.includes('browser')
-  }
-  // feature flags
-  Object.keys(featureFlags).forEach(key => {
-    vars[`process.env.${key}`] = featureFlags[key]
-  })
-  // build-specific env
-  if (opts.env) {
-    vars['process.env.NODE_ENV'] = JSON.stringify(opts.env)
-    vars.__DEV__ = opts.env !== 'production'
-  }
-
-  vars.preventAssignment = true
-  config.plugins.push(replace(vars))
-
-  Object.defineProperty(config, '_name', {
-    enumerable: false,
-    value: name
-  })
-
-  return config
-}
-
-if (process.env.TARGET) {
-  module.exports = genConfig(process.env.TARGET)
-} else {
-  exports.getBuild = genConfig
-  exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
-}
diff --git a/scripts/feature-flags.js b/scripts/feature-flags.js
deleted file mode 100644
index 7df14317ff1..00000000000
--- a/scripts/feature-flags.js
+++ /dev/null
@@ -1,4 +0,0 @@
-module.exports = {
-  NEW_SLOT_SYNTAX: true,
-  VBIND_PROP_SHORTHAND: false
-}
diff --git a/scripts/gen-release-note.js b/scripts/gen-release-note.js
deleted file mode 100644
index 4845c3102a5..00000000000
--- a/scripts/gen-release-note.js
+++ /dev/null
@@ -1,16 +0,0 @@
-const version = process.argv[2] || process.env.VERSION
-const cc = require('conventional-changelog')
-const file = `./RELEASE_NOTE${version ? `_${version}` : ``}.md`
-const fileStream = require('fs').createWriteStream(file)
-
-cc({
-  preset: 'angular',
-  pkg: {
-    transform (pkg) {
-      pkg.version = `v${version}`
-      return pkg
-    }
-  }
-}).pipe(fileStream).on('close', () => {
-  console.log(`Generated release note at ${file}`)
-})
diff --git a/scripts/git-hooks/commit-msg b/scripts/git-hooks/commit-msg
deleted file mode 100755
index d240e0b2647..00000000000
--- a/scripts/git-hooks/commit-msg
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-
-# Validate commit log
-commit_regex='^Merge.+|(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert|types)(\(.+\))?: .{1,50}'
-
-if ! grep -iqE "$commit_regex" "$1"; then
-    echo
-    echo "  Error: proper commit message format is required for automated changelog generation."
-    echo
-    echo "  - Use \`npm run commit\` to interactively generate a commit message."
-    echo "  - See .github/COMMIT_CONVENTION.md for more details."
-    echo
-    exit 1
-fi
diff --git a/scripts/git-hooks/pre-commit b/scripts/git-hooks/pre-commit
deleted file mode 100755
index 126d2ca400d..00000000000
--- a/scripts/git-hooks/pre-commit
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-
-files_to_lint=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js$')
-
-if [ -n "$files_to_lint" ]; then
-  NODE_ENV=production eslint --quiet $files_to_lint
-fi
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 03c5cf0ad26..00000000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,202 +0,0 @@
-const args = require('minimist')(process.argv.slice(2))
-const fs = require('fs')
-const path = require('path')
-const chalk = require('chalk')
-const semver = require('semver')
-const currentVersion = require('../package.json').version
-const { prompt } = require('enquirer')
-const execa = require('execa')
-
-const preId =
-  args.preid ||
-  (semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0])
-const isDryRun = args.dry
-const skipTests = args.skipTests
-const skipBuild = args.skipBuild
-const packages = fs
-  .readdirSync(path.resolve(__dirname, '../packages'))
-  .filter(p => !p.endsWith('.ts') && !p.startsWith('.'))
-  .concat('vue')
-
-const versionIncrements = [
-  'patch',
-  'minor',
-  'major',
-  ...(preId ? ['prepatch', 'preminor', 'premajor', 'prerelease'] : [])
-]
-
-const inc = i => semver.inc(currentVersion, i, preId)
-const run = (bin, args, opts = {}) =>
-  execa(bin, args, { stdio: 'inherit', ...opts })
-const dryRun = (bin, args, opts = {}) =>
-  console.log(chalk.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts)
-const runIfNotDry = isDryRun ? dryRun : run
-const step = msg => console.log(chalk.cyan(msg))
-
-async function main() {
-  let targetVersion = args._[0]
-
-  if (!targetVersion) {
-    // no explicit version, offer suggestions
-    const { release } = await prompt({
-      type: 'select',
-      name: 'release',
-      message: 'Select release type',
-      choices: versionIncrements.map(i => `${i} (${inc(i)})`).concat(['custom'])
-    })
-
-    if (release === 'custom') {
-      targetVersion = (
-        await prompt({
-          type: 'input',
-          name: 'version',
-          message: 'Input custom version',
-          initial: currentVersion
-        })
-      ).version
-    } else {
-      targetVersion = release.match(/\((.*)\)/)[1]
-    }
-  }
-
-  if (!semver.valid(targetVersion)) {
-    throw new Error(`invalid target version: ${targetVersion}`)
-  }
-
-  const { yes } = await prompt({
-    type: 'confirm',
-    name: 'yes',
-    message: `Releasing v${targetVersion}. Confirm?`
-  })
-
-  if (!yes) {
-    return
-  }
-
-  // run tests before release
-  step('\nRunning tests...')
-  if (!skipTests && !isDryRun) {
-    await run('pnpm', ['test'])
-  } else {
-    console.log(`(skipped)`)
-  }
-
-  // update all package versions and inter-dependencies
-  step('\nUpdating package versions...')
-  packages.forEach(p => updatePackage(getPkgRoot(p), targetVersion))
-
-  // build all packages with types
-  step('\nBuilding all packages...')
-  if (!skipBuild && !isDryRun) {
-    await run('pnpm', ['run', 'build'])
-    if (skipTests) {
-      await run('pnpm', ['run', 'build:types'])
-    }
-  } else {
-    console.log(`(skipped)`)
-  }
-
-  // generate changelog
-  step('\nGenerating changelog...')
-  await run(`pnpm`, ['run', 'changelog'])
-
-  // update pnpm-lock.yaml
-  step('\nUpdating lockfile...')
-  await run(`pnpm`, ['install', '--prefer-offline'])
-
-  const { stdout } = await run('git', ['diff'], { stdio: 'pipe' })
-  if (stdout) {
-    step('\nCommitting changes...')
-    await runIfNotDry('git', ['add', '-A'])
-    await runIfNotDry('git', ['commit', '-m', `release: v${targetVersion}`])
-  } else {
-    console.log('No changes to commit.')
-  }
-
-  // publish packages
-  step('\nPublishing packages...')
-  for (const pkg of packages) {
-    await publishPackage(pkg, targetVersion, runIfNotDry)
-  }
-
-  // push to GitHub
-  step('\nPushing to GitHub...')
-  await runIfNotDry('git', ['tag', `v${targetVersion}`])
-  await runIfNotDry('git', ['push', 'origin', `refs/tags/v${targetVersion}`])
-  await runIfNotDry('git', ['push'])
-
-  if (isDryRun) {
-    console.log(`\nDry run finished - run git diff to see package changes.`)
-  }
-  console.log()
-}
-
-function updatePackage(pkgRoot, version) {
-  const pkgPath = path.resolve(pkgRoot, 'package.json')
-  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
-  pkg.version = version
-  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
-}
-
-const getPkgRoot = pkg =>
-  pkg === 'vue'
-    ? path.resolve(__dirname, '../')
-    : path.resolve(__dirname, '../packages/' + pkg)
-
-async function publishPackage(pkgName, version, runIfNotDry) {
-  const pkgRoot = getPkgRoot(pkgName)
-  const pkgPath = path.resolve(pkgRoot, 'package.json')
-  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
-  const publishedName = pkg.name
-  if (pkg.private) {
-    return
-  }
-
-  let releaseTag = null
-  if (args.tag) {
-    releaseTag = args.tag
-  } else if (version.includes('alpha')) {
-    releaseTag = 'alpha'
-  } else if (version.includes('beta')) {
-    releaseTag = 'beta'
-  } else if (version.includes('rc')) {
-    releaseTag = 'rc'
-  }
-
-  // avoid overwriting tags for v3
-  if (pkgName === 'vue' || pkgName === 'compiler-sfc') {
-    if (releaseTag) {
-      releaseTag = `v2-${releaseTag}`
-    } else {
-      releaseTag = 'v2-latest'
-    }
-  }
-
-  step(`Publishing ${publishedName}...`)
-  try {
-    await runIfNotDry(
-      'pnpm',
-      [
-        'publish',
-        ...(releaseTag ? ['--tag', releaseTag] : []),
-        '--access',
-        'public'
-      ],
-      {
-        cwd: pkgRoot,
-        stdio: 'pipe'
-      }
-    )
-    console.log(
-      chalk.green(`Successfully published ${publishedName}@${version}`)
-    )
-  } catch (e) {
-    if (e.stderr.match(/previously published/)) {
-      console.log(chalk.red(`Skipping already published: ${publishedName}`))
-    } else {
-      throw e
-    }
-  }
-}
-
-main()
diff --git a/scripts/verify-commit-msg.js b/scripts/verify-commit-msg.js
deleted file mode 100644
index a5150a5f8e6..00000000000
--- a/scripts/verify-commit-msg.js
+++ /dev/null
@@ -1,24 +0,0 @@
-const chalk = require('chalk')
-const msgPath = process.env.GIT_PARAMS
-const msg = require('fs').readFileSync(msgPath, 'utf-8').trim()
-
-const commitRE =
-  /^(revert: )?(wip|release|feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types|build)(\(.+\))?: .{1,50}/
-
-if (!commitRE.test(msg)) {
-  console.log()
-  console.error(
-    `  ${chalk.bgRed.white(' ERROR ')} ${chalk.red(
-      `invalid commit message format.`
-    )}\n\n` +
-      chalk.red(
-        `  Proper commit message format is required for automated changelog generation. Examples:\n\n`
-      ) +
-      `    ${chalk.green(`feat(compiler): add 'comments' option`)}\n` +
-      `    ${chalk.green(
-        `fix(v-model): handle events on blur (close #28)`
-      )}\n\n` +
-      chalk.red(`  See .github/COMMIT_CONVENTION.md for more details.\n`)
-  )
-  process.exit(1)
-}
diff --git a/src/batcher.js b/src/batcher.js
new file mode 100644
index 00000000000..3f2e0eefe75
--- /dev/null
+++ b/src/batcher.js
@@ -0,0 +1,109 @@
+import config from './config'
+import {
+  warn,
+  nextTick,
+  devtools
+} from './util/index'
+
+// we have two separate queues: one for directive updates
+// and one for user watcher registered via $watch().
+// we want to guarantee directive updates to be called
+// before user watchers so that when user watchers are
+// triggered, the DOM would have already been in updated
+// state.
+
+var queue = []
+var userQueue = []
+var has = {}
+var circular = {}
+var waiting = false
+
+/**
+ * Reset the batcher's state.
+ */
+
+function resetBatcherState () {
+  queue.length = 0
+  userQueue.length = 0
+  has = {}
+  circular = {}
+  waiting = false
+}
+
+/**
+ * Flush both queues and run the watchers.
+ */
+
+function flushBatcherQueue () {
+  runBatcherQueue(queue)
+  runBatcherQueue(userQueue)
+  // user watchers triggered more watchers,
+  // keep flushing until it depletes
+  if (queue.length) {
+    return flushBatcherQueue()
+  }
+  // dev tool hook
+  /* istanbul ignore if */
+  if (devtools && config.devtools) {
+    devtools.emit('flush')
+  }
+  resetBatcherState()
+}
+
+/**
+ * Run the watchers in a single queue.
+ *
+ * @param {Array} queue
+ */
+
+function runBatcherQueue (queue) {
+  // do not cache length because more watchers might be pushed
+  // as we run existing watchers
+  for (let i = 0; i < queue.length; i++) {
+    var watcher = queue[i]
+    var id = watcher.id
+    has[id] = null
+    watcher.run()
+    // in dev build, check and stop circular updates.
+    if (process.env.NODE_ENV !== 'production' && has[id] != null) {
+      circular[id] = (circular[id] || 0) + 1
+      if (circular[id] > config._maxUpdateCount) {
+        warn(
+          'You may have an infinite update loop for watcher ' +
+          'with expression "' + watcher.expression + '"',
+          watcher.vm
+        )
+        break
+      }
+    }
+  }
+  queue.length = 0
+}
+
+/**
+ * Push a watcher into the watcher queue.
+ * Jobs with duplicate IDs will be skipped unless it's
+ * pushed when the queue is being flushed.
+ *
+ * @param {Watcher} watcher
+ *   properties:
+ *   - {Number} id
+ *   - {Function} run
+ */
+
+export function pushWatcher (watcher) {
+  const id = watcher.id
+  if (has[id] == null) {
+    // push watcher into appropriate queue
+    const q = watcher.user
+      ? userQueue
+      : queue
+    has[id] = q.length
+    q.push(watcher)
+    // queue the flush
+    if (!waiting) {
+      waiting = true
+      nextTick(flushBatcherQueue)
+    }
+  }
+}
diff --git a/src/cache.js b/src/cache.js
new file mode 100644
index 00000000000..7bf80c820ec
--- /dev/null
+++ b/src/cache.js
@@ -0,0 +1,117 @@
+/**
+ * A doubly linked list-based Least Recently Used (LRU)
+ * cache. Will keep most recently used items while
+ * discarding least recently used items when its limit is
+ * reached. This is a bare-bone version of
+ * Rasmus Andersson's js-lru:
+ *
+ *   https://github.com/rsms/js-lru
+ *
+ * @param {Number} limit
+ * @constructor
+ */
+
+export default function Cache (limit) {
+  this.size = 0
+  this.limit = limit
+  this.head = this.tail = undefined
+  this._keymap = Object.create(null)
+}
+
+var p = Cache.prototype
+
+/**
+ * Put <value> into the cache associated with <key>.
+ * Returns the entry which was removed to make room for
+ * the new entry. Otherwise undefined is returned.
+ * (i.e. if there was enough room already).
+ *
+ * @param {String} key
+ * @param {*} value
+ * @return {Entry|undefined}
+ */
+
+p.put = function (key, value) {
+  var removed
+
+  var entry = this.get(key, true)
+  if (!entry) {
+    if (this.size === this.limit) {
+      removed = this.shift()
+    }
+    entry = {
+      key: key
+    }
+    this._keymap[key] = entry
+    if (this.tail) {
+      this.tail.newer = entry
+      entry.older = this.tail
+    } else {
+      this.head = entry
+    }
+    this.tail = entry
+    this.size++
+  }
+  entry.value = value
+
+  return removed
+}
+
+/**
+ * Purge the least recently used (oldest) entry from the
+ * cache. Returns the removed entry or undefined if the
+ * cache was empty.
+ */
+
+p.shift = function () {
+  var entry = this.head
+  if (entry) {
+    this.head = this.head.newer
+    this.head.older = undefined
+    entry.newer = entry.older = undefined
+    this._keymap[entry.key] = undefined
+    this.size--
+  }
+  return entry
+}
+
+/**
+ * Get and register recent use of <key>. Returns the value
+ * associated with <key> or undefined if not in cache.
+ *
+ * @param {String} key
+ * @param {Boolean} returnEntry
+ * @return {Entry|*}
+ */
+
+p.get = function (key, returnEntry) {
+  var entry = this._keymap[key]
+  if (entry === undefined) return
+  if (entry === this.tail) {
+    return returnEntry
+      ? entry
+      : entry.value
+  }
+  // HEAD--------------TAIL
+  //   <.older   .newer>
+  //  <--- add direction --
+  //   A  B  C  <D>  E
+  if (entry.newer) {
+    if (entry === this.head) {
+      this.head = entry.newer
+    }
+    entry.newer.older = entry.older // C <-- E.
+  }
+  if (entry.older) {
+    entry.older.newer = entry.newer // C. --> E
+  }
+  entry.newer = undefined // D --x
+  entry.older = this.tail // D. --> E
+  if (this.tail) {
+    this.tail.newer = entry // E. <-- D
+  }
+  this.tail = entry
+  return returnEntry
+    ? entry
+    : entry.value
+}
diff --git a/src/compiler/codeframe.ts b/src/compiler/codeframe.ts
deleted file mode 100644
index 86b540ffc6f..00000000000
--- a/src/compiler/codeframe.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-const range = 2
-
-export function generateCodeFrame(
-  source: string,
-  start: number = 0,
-  end: number = source.length
-): string {
-  const lines = source.split(/\r?\n/)
-  let count = 0
-  const res: string[] = []
-  for (let i = 0; i < lines.length; i++) {
-    count += lines[i].length + 1
-    if (count >= start) {
-      for (let j = i - range; j <= i + range || end > count; j++) {
-        if (j < 0 || j >= lines.length) continue
-        res.push(
-          `${j + 1}${repeat(` `, 3 - String(j + 1).length)}|  ${lines[j]}`
-        )
-        const lineLength = lines[j].length
-        if (j === i) {
-          // push underline
-          const pad = start - (count - lineLength) + 1
-          const length = end > count ? lineLength - pad : end - start
-          res.push(`   |  ` + repeat(` `, pad) + repeat(`^`, length))
-        } else if (j > i) {
-          if (end > count) {
-            const length = Math.min(end - count, lineLength)
-            res.push(`   |  ` + repeat(`^`, length))
-          }
-          count += lineLength + 1
-        }
-      }
-      break
-    }
-  }
-  return res.join('\n')
-}
-
-function repeat(str: string, n: number) {
-  let result = ''
-  if (n > 0) {
-    // eslint-disable-next-line no-constant-condition
-    while (true) {
-      // eslint-disable-line
-      if (n & 1) result += str
-      n >>>= 1
-      if (n <= 0) break
-      str += str
-    }
-  }
-  return result
-}
diff --git a/src/compiler/codegen/events.ts b/src/compiler/codegen/events.ts
deleted file mode 100644
index 5ee53e54b17..00000000000
--- a/src/compiler/codegen/events.ts
+++ /dev/null
@@ -1,170 +0,0 @@
-import { ASTElementHandler, ASTElementHandlers } from 'types/compiler'
-
-const fnExpRE = /^([\w$_]+|\([^)]*?\))\s*=>|^function(?:\s+[\w$]+)?\s*\(/
-const fnInvokeRE = /\([^)]*?\);*$/
-const simplePathRE =
-  /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/
-
-// KeyboardEvent.keyCode aliases
-const keyCodes: { [key: string]: number | Array<number> } = {
-  esc: 27,
-  tab: 9,
-  enter: 13,
-  space: 32,
-  up: 38,
-  left: 37,
-  right: 39,
-  down: 40,
-  delete: [8, 46]
-}
-
-// KeyboardEvent.key aliases
-const keyNames: { [key: string]: string | Array<string> } = {
-  // #7880: IE11 and Edge use `Esc` for Escape key name.
-  esc: ['Esc', 'Escape'],
-  tab: 'Tab',
-  enter: 'Enter',
-  // #9112: IE11 uses `Spacebar` for Space key name.
-  space: [' ', 'Spacebar'],
-  // #7806: IE11 uses key names without `Arrow` prefix for arrow keys.
-  up: ['Up', 'ArrowUp'],
-  left: ['Left', 'ArrowLeft'],
-  right: ['Right', 'ArrowRight'],
-  down: ['Down', 'ArrowDown'],
-  // #9112: IE11 uses `Del` for Delete key name.
-  delete: ['Backspace', 'Delete', 'Del']
-}
-
-// #4868: modifiers that prevent the execution of the listener
-// need to explicitly return null so that we can determine whether to remove
-// the listener for .once
-const genGuard = condition => `if(${condition})return null;`
-
-const modifierCode: { [key: string]: string } = {
-  stop: '$event.stopPropagation();',
-  prevent: '$event.preventDefault();',
-  self: genGuard(`$event.target !== $event.currentTarget`),
-  ctrl: genGuard(`!$event.ctrlKey`),
-  shift: genGuard(`!$event.shiftKey`),
-  alt: genGuard(`!$event.altKey`),
-  meta: genGuard(`!$event.metaKey`),
-  left: genGuard(`'button' in $event && $event.button !== 0`),
-  middle: genGuard(`'button' in $event && $event.button !== 1`),
-  right: genGuard(`'button' in $event && $event.button !== 2`)
-}
-
-export function genHandlers(
-  events: ASTElementHandlers,
-  isNative: boolean
-): string {
-  const prefix = isNative ? 'nativeOn:' : 'on:'
-  let staticHandlers = ``
-  let dynamicHandlers = ``
-  for (const name in events) {
-    const handlerCode = genHandler(events[name])
-    //@ts-expect-error
-    if (events[name] && events[name].dynamic) {
-      dynamicHandlers += `${name},${handlerCode},`
-    } else {
-      staticHandlers += `"${name}":${handlerCode},`
-    }
-  }
-  staticHandlers = `{${staticHandlers.slice(0, -1)}}`
-  if (dynamicHandlers) {
-    return prefix + `_d(${staticHandlers},[${dynamicHandlers.slice(0, -1)}])`
-  } else {
-    return prefix + staticHandlers
-  }
-}
-
-function genHandler(
-  handler: ASTElementHandler | Array<ASTElementHandler>
-): string {
-  if (!handler) {
-    return 'function(){}'
-  }
-
-  if (Array.isArray(handler)) {
-    return `[${handler.map(handler => genHandler(handler)).join(',')}]`
-  }
-
-  const isMethodPath = simplePathRE.test(handler.value)
-  const isFunctionExpression = fnExpRE.test(handler.value)
-  const isFunctionInvocation = simplePathRE.test(
-    handler.value.replace(fnInvokeRE, '')
-  )
-
-  if (!handler.modifiers) {
-    if (isMethodPath || isFunctionExpression) {
-      return handler.value
-    }
-    return `function($event){${
-      isFunctionInvocation ? `return ${handler.value}` : handler.value
-    }}` // inline statement
-  } else {
-    let code = ''
-    let genModifierCode = ''
-    const keys: string[] = []
-    for (const key in handler.modifiers) {
-      if (modifierCode[key]) {
-        genModifierCode += modifierCode[key]
-        // left/right
-        if (keyCodes[key]) {
-          keys.push(key)
-        }
-      } else if (key === 'exact') {
-        const modifiers = handler.modifiers
-        genModifierCode += genGuard(
-          ['ctrl', 'shift', 'alt', 'meta']
-            .filter(keyModifier => !modifiers[keyModifier])
-            .map(keyModifier => `$event.${keyModifier}Key`)
-            .join('||')
-        )
-      } else {
-        keys.push(key)
-      }
-    }
-    if (keys.length) {
-      code += genKeyFilter(keys)
-    }
-    // Make sure modifiers like prevent and stop get executed after key filtering
-    if (genModifierCode) {
-      code += genModifierCode
-    }
-    const handlerCode = isMethodPath
-      ? `return ${handler.value}.apply(null, arguments)`
-      : isFunctionExpression
-      ? `return (${handler.value}).apply(null, arguments)`
-      : isFunctionInvocation
-      ? `return ${handler.value}`
-      : handler.value
-    return `function($event){${code}${handlerCode}}`
-  }
-}
-
-function genKeyFilter(keys: Array<string>): string {
-  return (
-    // make sure the key filters only apply to KeyboardEvents
-    // #9441: can't use 'keyCode' in $event because Chrome autofill fires fake
-    // key events that do not have keyCode property...
-    `if(!$event.type.indexOf('key')&&` +
-    `${keys.map(genFilterCode).join('&&')})return null;`
-  )
-}
-
-function genFilterCode(key: string): string {
-  const keyVal = parseInt(key, 10)
-  if (keyVal) {
-    return `$event.keyCode!==${keyVal}`
-  }
-  const keyCode = keyCodes[key]
-  const keyName = keyNames[key]
-  return (
-    `_k($event.keyCode,` +
-    `${JSON.stringify(key)},` +
-    `${JSON.stringify(keyCode)},` +
-    `$event.key,` +
-    `${JSON.stringify(keyName)}` +
-    `)`
-  )
-}
diff --git a/src/compiler/codegen/index.ts b/src/compiler/codegen/index.ts
deleted file mode 100644
index b0daf352f82..00000000000
--- a/src/compiler/codegen/index.ts
+++ /dev/null
@@ -1,668 +0,0 @@
-import { genHandlers } from './events'
-import baseDirectives from '../directives/index'
-import { camelize, no, extend, capitalize } from 'shared/util'
-import { baseWarn, pluckModuleFunction } from '../helpers'
-import { emptySlotScopeToken } from '../parser/index'
-import {
-  ASTAttr,
-  ASTDirective,
-  ASTElement,
-  ASTExpression,
-  ASTIfConditions,
-  ASTNode,
-  ASTText,
-  CompilerOptions
-} from 'types/compiler'
-import { BindingMetadata, BindingTypes } from 'sfc/types'
-
-type TransformFunction = (el: ASTElement, code: string) => string
-type DataGenFunction = (el: ASTElement) => string
-type DirectiveFunction = (
-  el: ASTElement,
-  dir: ASTDirective,
-  warn: Function
-) => boolean
-
-export class CodegenState {
-  options: CompilerOptions
-  warn: Function
-  transforms: Array<TransformFunction>
-  dataGenFns: Array<DataGenFunction>
-  directives: { [key: string]: DirectiveFunction }
-  maybeComponent: (el: ASTElement) => boolean
-  onceId: number
-  staticRenderFns: Array<string>
-  pre: boolean
-
-  constructor(options: CompilerOptions) {
-    this.options = options
-    this.warn = options.warn || baseWarn
-    this.transforms = pluckModuleFunction(options.modules, 'transformCode')
-    this.dataGenFns = pluckModuleFunction(options.modules, 'genData')
-    this.directives = extend(extend({}, baseDirectives), options.directives)
-    const isReservedTag = options.isReservedTag || no
-    this.maybeComponent = (el: ASTElement) =>
-      !!el.component || !isReservedTag(el.tag)
-    this.onceId = 0
-    this.staticRenderFns = []
-    this.pre = false
-  }
-}
-
-export type CodegenResult = {
-  render: string
-  staticRenderFns: Array<string>
-}
-
-export function generate(
-  ast: ASTElement | void,
-  options: CompilerOptions
-): CodegenResult {
-  const state = new CodegenState(options)
-  // fix #11483, Root level <script> tags should not be rendered.
-  const code = ast
-    ? ast.tag === 'script'
-      ? 'null'
-      : genElement(ast, state)
-    : '_c("div")'
-  return {
-    render: `with(this){return ${code}}`,
-    staticRenderFns: state.staticRenderFns
-  }
-}
-
-export function genElement(el: ASTElement, state: CodegenState): string {
-  if (el.parent) {
-    el.pre = el.pre || el.parent.pre
-  }
-
-  if (el.staticRoot && !el.staticProcessed) {
-    return genStatic(el, state)
-  } else if (el.once && !el.onceProcessed) {
-    return genOnce(el, state)
-  } else if (el.for && !el.forProcessed) {
-    return genFor(el, state)
-  } else if (el.if && !el.ifProcessed) {
-    return genIf(el, state)
-  } else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
-    return genChildren(el, state) || 'void 0'
-  } else if (el.tag === 'slot') {
-    return genSlot(el, state)
-  } else {
-    // component or element
-    let code
-    if (el.component) {
-      code = genComponent(el.component, el, state)
-    } else {
-      let data
-      const maybeComponent = state.maybeComponent(el)
-      if (!el.plain || (el.pre && maybeComponent)) {
-        data = genData(el, state)
-      }
-
-      let tag: string | undefined
-      // check if this is a component in <script setup>
-      const bindings = state.options.bindings
-      if (maybeComponent && bindings && bindings.__isScriptSetup !== false) {
-        tag = checkBindingType(bindings, el.tag)
-      }
-      if (!tag) tag = `'${el.tag}'`
-
-      const children = el.inlineTemplate ? null : genChildren(el, state, true)
-      code = `_c(${tag}${
-        data ? `,${data}` : '' // data
-      }${
-        children ? `,${children}` : '' // children
-      })`
-    }
-    // module transforms
-    for (let i = 0; i < state.transforms.length; i++) {
-      code = state.transforms[i](el, code)
-    }
-    return code
-  }
-}
-
-function checkBindingType(bindings: BindingMetadata, key: string) {
-  const camelName = camelize(key)
-  const PascalName = capitalize(camelName)
-  const checkType = (type) => {
-    if (bindings[key] === type) {
-      return key
-    }
-    if (bindings[camelName] === type) {
-      return camelName
-    }
-    if (bindings[PascalName] === type) {
-      return PascalName
-    }
-  }
-  const fromConst =
-    checkType(BindingTypes.SETUP_CONST) ||
-    checkType(BindingTypes.SETUP_REACTIVE_CONST)
-  if (fromConst) {
-    return fromConst
-  }
-
-  const fromMaybeRef =
-    checkType(BindingTypes.SETUP_LET) ||
-    checkType(BindingTypes.SETUP_REF) ||
-    checkType(BindingTypes.SETUP_MAYBE_REF)
-  if (fromMaybeRef) {
-    return fromMaybeRef
-  }
-}
-
-// hoist static sub-trees out
-function genStatic(el: ASTElement, state: CodegenState): string {
-  el.staticProcessed = true
-  // Some elements (templates) need to behave differently inside of a v-pre
-  // node.  All pre nodes are static roots, so we can use this as a location to
-  // wrap a state change and reset it upon exiting the pre node.
-  const originalPreState = state.pre
-  if (el.pre) {
-    state.pre = el.pre
-  }
-  state.staticRenderFns.push(`with(this){return ${genElement(el, state)}}`)
-  state.pre = originalPreState
-  return `_m(${state.staticRenderFns.length - 1}${
-    el.staticInFor ? ',true' : ''
-  })`
-}
-
-// v-once
-function genOnce(el: ASTElement, state: CodegenState): string {
-  el.onceProcessed = true
-  if (el.if && !el.ifProcessed) {
-    return genIf(el, state)
-  } else if (el.staticInFor) {
-    let key = ''
-    let parent = el.parent
-    while (parent) {
-      if (parent.for) {
-        key = parent.key!
-        break
-      }
-      parent = parent.parent
-    }
-    if (!key) {
-      __DEV__ &&
-        state.warn(
-          `v-once can only be used inside v-for that is keyed. `,
-          el.rawAttrsMap['v-once']
-        )
-      return genElement(el, state)
-    }
-    return `_o(${genElement(el, state)},${state.onceId++},${key})`
-  } else {
-    return genStatic(el, state)
-  }
-}
-
-export function genIf(
-  el: any,
-  state: CodegenState,
-  altGen?: Function,
-  altEmpty?: string
-): string {
-  el.ifProcessed = true // avoid recursion
-  return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)
-}
-
-function genIfConditions(
-  conditions: ASTIfConditions,
-  state: CodegenState,
-  altGen?: Function,
-  altEmpty?: string
-): string {
-  if (!conditions.length) {
-    return altEmpty || '_e()'
-  }
-
-  const condition = conditions.shift()!
-  if (condition.exp) {
-    return `(${condition.exp})?${genTernaryExp(
-      condition.block
-    )}:${genIfConditions(conditions, state, altGen, altEmpty)}`
-  } else {
-    return `${genTernaryExp(condition.block)}`
-  }
-
-  // v-if with v-once should generate code like (a)?_m(0):_m(1)
-  function genTernaryExp(el) {
-    return altGen
-      ? altGen(el, state)
-      : el.once
-      ? genOnce(el, state)
-      : genElement(el, state)
-  }
-}
-
-export function genFor(
-  el: any,
-  state: CodegenState,
-  altGen?: Function,
-  altHelper?: string
-): string {
-  const exp = el.for
-  const alias = el.alias
-  const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
-  const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''
-
-  if (
-    __DEV__ &&
-    state.maybeComponent(el) &&
-    el.tag !== 'slot' &&
-    el.tag !== 'template' &&
-    !el.key
-  ) {
-    state.warn(
-      `<${el.tag} v-for="${alias} in ${exp}">: component lists rendered with ` +
-        `v-for should have explicit keys. ` +
-        `See https://v2.vuejs.org/v2/guide/list.html#key for more info.`,
-      el.rawAttrsMap['v-for'],
-      true /* tip */
-    )
-  }
-
-  el.forProcessed = true // avoid recursion
-  return (
-    `${altHelper || '_l'}((${exp}),` +
-    `function(${alias}${iterator1}${iterator2}){` +
-    `return ${(altGen || genElement)(el, state)}` +
-    '})'
-  )
-}
-
-export function genData(el: ASTElement, state: CodegenState): string {
-  let data = '{'
-
-  // directives first.
-  // directives may mutate the el's other properties before they are generated.
-  const dirs = genDirectives(el, state)
-  if (dirs) data += dirs + ','
-
-  // key
-  if (el.key) {
-    data += `key:${el.key},`
-  }
-  // ref
-  if (el.ref) {
-    data += `ref:${el.ref},`
-  }
-  if (el.refInFor) {
-    data += `refInFor:true,`
-  }
-  // pre
-  if (el.pre) {
-    data += `pre:true,`
-  }
-  // record original tag name for components using "is" attribute
-  if (el.component) {
-    data += `tag:"${el.tag}",`
-  }
-  // module data generation functions
-  for (let i = 0; i < state.dataGenFns.length; i++) {
-    data += state.dataGenFns[i](el)
-  }
-  // attributes
-  if (el.attrs) {
-    data += `attrs:${genProps(el.attrs)},`
-  }
-  // DOM props
-  if (el.props) {
-    data += `domProps:${genProps(el.props)},`
-  }
-  // event handlers
-  if (el.events) {
-    data += `${genHandlers(el.events, false)},`
-  }
-  if (el.nativeEvents) {
-    data += `${genHandlers(el.nativeEvents, true)},`
-  }
-  // slot target
-  // only for non-scoped slots
-  if (el.slotTarget && !el.slotScope) {
-    data += `slot:${el.slotTarget},`
-  }
-  // scoped slots
-  if (el.scopedSlots) {
-    data += `${genScopedSlots(el, el.scopedSlots, state)},`
-  }
-  // component v-model
-  if (el.model) {
-    data += `model:{value:${el.model.value},callback:${el.model.callback},expression:${el.model.expression}},`
-  }
-  // inline-template
-  if (el.inlineTemplate) {
-    const inlineTemplate = genInlineTemplate(el, state)
-    if (inlineTemplate) {
-      data += `${inlineTemplate},`
-    }
-  }
-  data = data.replace(/,$/, '') + '}'
-  // v-bind dynamic argument wrap
-  // v-bind with dynamic arguments must be applied using the same v-bind object
-  // merge helper so that class/style/mustUseProp attrs are handled correctly.
-  if (el.dynamicAttrs) {
-    data = `_b(${data},"${el.tag}",${genProps(el.dynamicAttrs)})`
-  }
-  // v-bind data wrap
-  if (el.wrapData) {
-    data = el.wrapData(data)
-  }
-  // v-on data wrap
-  if (el.wrapListeners) {
-    data = el.wrapListeners(data)
-  }
-  return data
-}
-
-function genDirectives(el: ASTElement, state: CodegenState): string | void {
-  const dirs = el.directives
-  if (!dirs) return
-  let res = 'directives:['
-  let hasRuntime = false
-  let i, l, dir, needRuntime
-  for (i = 0, l = dirs.length; i < l; i++) {
-    dir = dirs[i]
-    needRuntime = true
-    const gen: DirectiveFunction = state.directives[dir.name]
-    if (gen) {
-      // compile-time directive that manipulates AST.
-      // returns true if it also needs a runtime counterpart.
-      needRuntime = !!gen(el, dir, state.warn)
-    }
-    if (needRuntime) {
-      hasRuntime = true
-      res += `{name:"${dir.name}",rawName:"${dir.rawName}"${
-        dir.value
-          ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}`
-          : ''
-      }${dir.arg ? `,arg:${dir.isDynamicArg ? dir.arg : `"${dir.arg}"`}` : ''}${
-        dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : ''
-      }},`
-    }
-  }
-  if (hasRuntime) {
-    return res.slice(0, -1) + ']'
-  }
-}
-
-function genInlineTemplate(
-  el: ASTElement,
-  state: CodegenState
-): string | undefined {
-  const ast = el.children[0]
-  if (__DEV__ && (el.children.length !== 1 || ast.type !== 1)) {
-    state.warn(
-      'Inline-template components must have exactly one child element.',
-      { start: el.start }
-    )
-  }
-  if (ast && ast.type === 1) {
-    const inlineRenderFns = generate(ast, state.options)
-    return `inlineTemplate:{render:function(){${
-      inlineRenderFns.render
-    }},staticRenderFns:[${inlineRenderFns.staticRenderFns
-      .map(code => `function(){${code}}`)
-      .join(',')}]}`
-  }
-}
-
-function genScopedSlots(
-  el: ASTElement,
-  slots: { [key: string]: ASTElement },
-  state: CodegenState
-): string {
-  // by default scoped slots are considered "stable", this allows child
-  // components with only scoped slots to skip forced updates from parent.
-  // but in some cases we have to bail-out of this optimization
-  // for example if the slot contains dynamic names, has v-if or v-for on them...
-  let needsForceUpdate =
-    el.for ||
-    Object.keys(slots).some(key => {
-      const slot = slots[key]
-      return (
-        slot.slotTargetDynamic || slot.if || slot.for || containsSlotChild(slot) // is passing down slot from parent which may be dynamic
-      )
-    })
-
-  // #9534: if a component with scoped slots is inside a conditional branch,
-  // it's possible for the same component to be reused but with different
-  // compiled slot content. To avoid that, we generate a unique key based on
-  // the generated code of all the slot contents.
-  let needsKey = !!el.if
-
-  // OR when it is inside another scoped slot or v-for (the reactivity may be
-  // disconnected due to the intermediate scope variable)
-  // #9438, #9506
-  // TODO: this can be further optimized by properly analyzing in-scope bindings
-  // and skip force updating ones that do not actually use scope variables.
-  if (!needsForceUpdate) {
-    let parent = el.parent
-    while (parent) {
-      if (
-        (parent.slotScope && parent.slotScope !== emptySlotScopeToken) ||
-        parent.for
-      ) {
-        needsForceUpdate = true
-        break
-      }
-      if (parent.if) {
-        needsKey = true
-      }
-      parent = parent.parent
-    }
-  }
-
-  const generatedSlots = Object.keys(slots)
-    .map(key => genScopedSlot(slots[key], state))
-    .join(',')
-
-  return `scopedSlots:_u([${generatedSlots}]${
-    needsForceUpdate ? `,null,true` : ``
-  }${
-    !needsForceUpdate && needsKey ? `,null,false,${hash(generatedSlots)}` : ``
-  })`
-}
-
-function hash(str) {
-  let hash = 5381
-  let i = str.length
-  while (i) {
-    hash = (hash * 33) ^ str.charCodeAt(--i)
-  }
-  return hash >>> 0
-}
-
-function containsSlotChild(el: ASTNode): boolean {
-  if (el.type === 1) {
-    if (el.tag === 'slot') {
-      return true
-    }
-    return el.children.some(containsSlotChild)
-  }
-  return false
-}
-
-function genScopedSlot(el: ASTElement, state: CodegenState): string {
-  const isLegacySyntax = el.attrsMap['slot-scope']
-  if (el.if && !el.ifProcessed && !isLegacySyntax) {
-    return genIf(el, state, genScopedSlot, `null`)
-  }
-  if (el.for && !el.forProcessed) {
-    return genFor(el, state, genScopedSlot)
-  }
-  const slotScope =
-    el.slotScope === emptySlotScopeToken ? `` : String(el.slotScope)
-  const fn =
-    `function(${slotScope}){` +
-    `return ${
-      el.tag === 'template'
-        ? el.if && isLegacySyntax
-          ? `(${el.if})?${genChildren(el, state) || 'undefined'}:undefined`
-          : genChildren(el, state) || 'undefined'
-        : genElement(el, state)
-    }}`
-  // reverse proxy v-slot without scope on this.$slots
-  const reverseProxy = slotScope ? `` : `,proxy:true`
-  return `{key:${el.slotTarget || `"default"`},fn:${fn}${reverseProxy}}`
-}
-
-export function genChildren(
-  el: ASTElement,
-  state: CodegenState,
-  checkSkip?: boolean,
-  altGenElement?: Function,
-  altGenNode?: Function
-): string | void {
-  const children = el.children
-  if (children.length) {
-    const el: any = children[0]
-    // optimize single v-for
-    if (
-      children.length === 1 &&
-      el.for &&
-      el.tag !== 'template' &&
-      el.tag !== 'slot'
-    ) {
-      const normalizationType = checkSkip
-        ? state.maybeComponent(el)
-          ? `,1`
-          : `,0`
-        : ``
-      return `${(altGenElement || genElement)(el, state)}${normalizationType}`
-    }
-    const normalizationType = checkSkip
-      ? getNormalizationType(children, state.maybeComponent)
-      : 0
-    const gen = altGenNode || genNode
-    return `[${children.map(c => gen(c, state)).join(',')}]${
-      normalizationType ? `,${normalizationType}` : ''
-    }`
-  }
-}
-
-// determine the normalization needed for the children array.
-// 0: no normalization needed
-// 1: simple normalization needed (possible 1-level deep nested array)
-// 2: full normalization needed
-function getNormalizationType(
-  children: Array<ASTNode>,
-  maybeComponent: (el: ASTElement) => boolean
-): number {
-  let res = 0
-  for (let i = 0; i < children.length; i++) {
-    const el: ASTNode = children[i]
-    if (el.type !== 1) {
-      continue
-    }
-    if (
-      needsNormalization(el) ||
-      (el.ifConditions &&
-        el.ifConditions.some(c => needsNormalization(c.block)))
-    ) {
-      res = 2
-      break
-    }
-    if (
-      maybeComponent(el) ||
-      (el.ifConditions && el.ifConditions.some(c => maybeComponent(c.block)))
-    ) {
-      res = 1
-    }
-  }
-  return res
-}
-
-function needsNormalization(el: ASTElement): boolean {
-  return el.for !== undefined || el.tag === 'template' || el.tag === 'slot'
-}
-
-function genNode(node: ASTNode, state: CodegenState): string {
-  if (node.type === 1) {
-    return genElement(node, state)
-  } else if (node.type === 3 && node.isComment) {
-    return genComment(node)
-  } else {
-    return genText(node)
-  }
-}
-
-export function genText(text: ASTText | ASTExpression): string {
-  return `_v(${
-    text.type === 2
-      ? text.expression // no need for () because already wrapped in _s()
-      : transformSpecialNewlines(JSON.stringify(text.text))
-  })`
-}
-
-export function genComment(comment: ASTText): string {
-  return `_e(${JSON.stringify(comment.text)})`
-}
-
-function genSlot(el: ASTElement, state: CodegenState): string {
-  const slotName = el.slotName || '"default"'
-  const children = genChildren(el, state)
-  let res = `_t(${slotName}${children ? `,function(){return ${children}}` : ''}`
-  const attrs =
-    el.attrs || el.dynamicAttrs
-      ? genProps(
-          (el.attrs || []).concat(el.dynamicAttrs || []).map(attr => ({
-            // slot props are camelized
-            name: camelize(attr.name),
-            value: attr.value,
-            dynamic: attr.dynamic
-          }))
-        )
-      : null
-  const bind = el.attrsMap['v-bind']
-  if ((attrs || bind) && !children) {
-    res += `,null`
-  }
-  if (attrs) {
-    res += `,${attrs}`
-  }
-  if (bind) {
-    res += `${attrs ? '' : ',null'},${bind}`
-  }
-  return res + ')'
-}
-
-// componentName is el.component, take it as argument to shun flow's pessimistic refinement
-function genComponent(
-  componentName: string,
-  el: ASTElement,
-  state: CodegenState
-): string {
-  const children = el.inlineTemplate ? null : genChildren(el, state, true)
-  return `_c(${componentName},${genData(el, state)}${
-    children ? `,${children}` : ''
-  })`
-}
-
-function genProps(props: Array<ASTAttr>): string {
-  let staticProps = ``
-  let dynamicProps = ``
-  for (let i = 0; i < props.length; i++) {
-    const prop = props[i]
-    const value = transformSpecialNewlines(prop.value)
-    if (prop.dynamic) {
-      dynamicProps += `${prop.name},${value},`
-    } else {
-      staticProps += `"${prop.name}":${value},`
-    }
-  }
-  staticProps = `{${staticProps.slice(0, -1)}}`
-  if (dynamicProps) {
-    return `_d(${staticProps},[${dynamicProps.slice(0, -1)}])`
-  } else {
-    return staticProps
-  }
-}
-
-// #3895, #4268
-function transformSpecialNewlines(text: string): string {
-  return text.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029')
-}
diff --git a/src/compiler/compile-props.js b/src/compiler/compile-props.js
new file mode 100644
index 00000000000..99271120833
--- /dev/null
+++ b/src/compiler/compile-props.js
@@ -0,0 +1,462 @@
+import config from '../config'
+import { parseDirective } from '../parsers/directive'
+import { isSimplePath } from '../parsers/expression'
+import { defineReactive, withoutConversion } from '../observer/index'
+import propDef from '../directives/internal/prop'
+import {
+  warn,
+  camelize,
+  hyphenate,
+  getAttr,
+  getBindAttr,
+  isLiteral,
+  toBoolean,
+  toNumber,
+  stripQuotes,
+  isArray,
+  isPlainObject,
+  isObject,
+  hasOwn
+} from '../util/index'
+
+const propBindingModes = config._propBindingModes
+const empty = {}
+
+// regexes
+const identRE = /^[$_a-zA-Z]+[\w$]*$/
+const settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/
+
+/**
+ * Compile props on a root element and return
+ * a props link function.
+ *
+ * @param {Element|DocumentFragment} el
+ * @param {Array} propOptions
+ * @param {Vue} vm
+ * @return {Function} propsLinkFn
+ */
+
+export function compileProps (el, propOptions, vm) {
+  var props = []
+  var propsData = vm.$options.propsData
+  var names = Object.keys(propOptions)
+  var i = names.length
+  var options, name, attr, value, path, parsed, prop
+  while (i--) {
+    name = names[i]
+    options = propOptions[name] || empty
+
+    if (process.env.NODE_ENV !== 'production' && name === '$data') {
+      warn('Do not use $data as prop.', vm)
+      continue
+    }
+
+    // props could contain dashes, which will be
+    // interpreted as minus calculations by the parser
+    // so we need to camelize the path here
+    path = camelize(name)
+    if (!identRE.test(path)) {
+      process.env.NODE_ENV !== 'production' && warn(
+        'Invalid prop key: "' + name + '". Prop keys ' +
+        'must be valid identifiers.',
+        vm
+      )
+      continue
+    }
+
+    prop = {
+      name: name,
+      path: path,
+      options: options,
+      mode: propBindingModes.ONE_WAY,
+      raw: null
+    }
+
+    attr = hyphenate(name)
+    // first check dynamic version
+    if ((value = getBindAttr(el, attr)) === null) {
+      if ((value = getBindAttr(el, attr + '.sync')) !== null) {
+        prop.mode = propBindingModes.TWO_WAY
+      } else if ((value = getBindAttr(el, attr + '.once')) !== null) {
+        prop.mode = propBindingModes.ONE_TIME
+      }
+    }
+    if (value !== null) {
+      // has dynamic binding!
+      prop.raw = value
+      parsed = parseDirective(value)
+      value = parsed.expression
+      prop.filters = parsed.filters
+      // check binding type
+      if (isLiteral(value) && !parsed.filters) {
+        // for expressions containing literal numbers and
+        // booleans, there's no need to setup a prop binding,
+        // so we can optimize them as a one-time set.
+        prop.optimizedLiteral = true
+      } else {
+        prop.dynamic = true
+        // check non-settable path for two-way bindings
+        if (process.env.NODE_ENV !== 'production' &&
+            prop.mode === propBindingModes.TWO_WAY &&
+            !settablePathRE.test(value)) {
+          prop.mode = propBindingModes.ONE_WAY
+          warn(
+            'Cannot bind two-way prop with non-settable ' +
+            'parent path: ' + value,
+            vm
+          )
+        }
+      }
+      prop.parentPath = value
+
+      // warn required two-way
+      if (
+        process.env.NODE_ENV !== 'production' &&
+        options.twoWay &&
+        prop.mode !== propBindingModes.TWO_WAY
+      ) {
+        warn(
+          'Prop "' + name + '" expects a two-way binding type.',
+          vm
+        )
+      }
+    } else if ((value = getAttr(el, attr)) !== null) {
+      // has literal binding!
+      prop.raw = value
+    } else if (propsData && ((value = propsData[name] || propsData[path]) !== null)) {
+      // has propsData
+      prop.raw = value
+    } else if (process.env.NODE_ENV !== 'production') {
+      // check possible camelCase prop usage
+      var lowerCaseName = path.toLowerCase()
+      value = /[A-Z\-]/.test(name) && (
+        el.getAttribute(lowerCaseName) ||
+        el.getAttribute(':' + lowerCaseName) ||
+        el.getAttribute('v-bind:' + lowerCaseName) ||
+        el.getAttribute(':' + lowerCaseName + '.once') ||
+        el.getAttribute('v-bind:' + lowerCaseName + '.once') ||
+        el.getAttribute(':' + lowerCaseName + '.sync') ||
+        el.getAttribute('v-bind:' + lowerCaseName + '.sync')
+      )
+      if (value) {
+        warn(
+          'Possible usage error for prop `' + lowerCaseName + '` - ' +
+          'did you mean `' + attr + '`? HTML is case-insensitive, remember to use ' +
+          'kebab-case for props in templates.',
+          vm
+        )
+      } else if (options.required && (
+        !propsData || (
+          !(name in propsData) &&
+          !(path in propsData)
+        )
+      )) {
+        // warn missing required
+        warn('Missing required prop: ' + name, vm)
+      }
+    }
+    // push prop
+    props.push(prop)
+  }
+  return makePropsLinkFn(props)
+}
+
+/**
+ * Build a function that applies props to a vm.
+ *
+ * @param {Array} props
+ * @return {Function} propsLinkFn
+ */
+
+function makePropsLinkFn (props) {
+  return function propsLinkFn (vm, scope) {
+    // store resolved props info
+    vm._props = {}
+    var inlineProps = vm.$options.propsData
+    var i = props.length
+    var prop, path, options, value, raw
+    while (i--) {
+      prop = props[i]
+      raw = prop.raw
+      path = prop.path
+      options = prop.options
+      vm._props[path] = prop
+      if (inlineProps && hasOwn(inlineProps, path)) {
+        initProp(vm, prop, inlineProps[path])
+      } if (raw === null) {
+        // initialize absent prop
+        initProp(vm, prop, undefined)
+      } else if (prop.dynamic) {
+        // dynamic prop
+        if (prop.mode === propBindingModes.ONE_TIME) {
+          // one time binding
+          value = (scope || vm._context || vm).$get(prop.parentPath)
+          initProp(vm, prop, value)
+        } else {
+          if (vm._context) {
+            // dynamic binding
+            vm._bindDir({
+              name: 'prop',
+              def: propDef,
+              prop: prop
+            }, null, null, scope) // el, host, scope
+          } else {
+            // root instance
+            initProp(vm, prop, vm.$get(prop.parentPath))
+          }
+        }
+      } else if (prop.optimizedLiteral) {
+        // optimized literal, cast it and just set once
+        var stripped = stripQuotes(raw)
+        value = stripped === raw
+          ? toBoolean(toNumber(raw))
+          : stripped
+        initProp(vm, prop, value)
+      } else {
+        // string literal, but we need to cater for
+        // Boolean props with no value, or with same
+        // literal value (e.g. disabled="disabled")
+        // see https://github.com/vuejs/vue-loader/issues/182
+        value = (
+          options.type === Boolean &&
+          (raw === '' || raw === hyphenate(prop.name))
+        ) ? true
+          : raw
+        initProp(vm, prop, value)
+      }
+    }
+  }
+}
+
+/**
+ * Process a prop with a rawValue, applying necessary coersions,
+ * default values & assertions and call the given callback with
+ * processed value.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @param {*} rawValue
+ * @param {Function} fn
+ */
+
+function processPropValue (vm, prop, rawValue, fn) {
+  const isSimple = prop.dynamic && isSimplePath(prop.parentPath)
+  let value = rawValue
+  if (value === undefined) {
+    value = getPropDefaultValue(vm, prop)
+  }
+  value = coerceProp(prop, value, vm)
+  const coerced = value !== rawValue
+  if (!assertProp(prop, value, vm)) {
+    value = undefined
+  }
+  if (isSimple && !coerced) {
+    withoutConversion(() => {
+      fn(value)
+    })
+  } else {
+    fn(value)
+  }
+}
+
+/**
+ * Set a prop's initial value on a vm and its data object.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @param {*} value
+ */
+
+export function initProp (vm, prop, value) {
+  processPropValue(vm, prop, value, value => {
+    defineReactive(vm, prop.path, value)
+  })
+}
+
+/**
+ * Update a prop's value on a vm.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @param {*} value
+ */
+
+export function updateProp (vm, prop, value) {
+  processPropValue(vm, prop, value, value => {
+    vm[prop.path] = value
+  })
+}
+
+/**
+ * Get the default value of a prop.
+ *
+ * @param {Vue} vm
+ * @param {Object} prop
+ * @return {*}
+ */
+
+function getPropDefaultValue (vm, prop) {
+  // no default, return undefined
+  const options = prop.options
+  if (!hasOwn(options, 'default')) {
+    // absent boolean value defaults to false
+    return options.type === Boolean
+      ? false
+      : undefined
+  }
+  var def = options.default
+  // warn against non-factory defaults for Object & Array
+  if (isObject(def)) {
+    process.env.NODE_ENV !== 'production' && warn(
+      'Invalid default value for prop "' + prop.name + '": ' +
+      'Props with type Object/Array must use a factory function ' +
+      'to return the default value.',
+      vm
+    )
+  }
+  // call factory function for non-Function types
+  return typeof def === 'function' && options.type !== Function
+    ? def.call(vm)
+    : def
+}
+
+/**
+ * Assert whether a prop is valid.
+ *
+ * @param {Object} prop
+ * @param {*} value
+ * @param {Vue} vm
+ */
+
+function assertProp (prop, value, vm) {
+  if (
+    !prop.options.required && ( // non-required
+      prop.raw === null ||      // abscent
+      value == null             // null or undefined
+    )
+  ) {
+    return true
+  }
+  var options = prop.options
+  var type = options.type
+  var valid = !type
+  var expectedTypes = []
+  if (type) {
+    if (!isArray(type)) {
+      type = [type]
+    }
+    for (var i = 0; i < type.length && !valid; i++) {
+      var assertedType = assertType(value, type[i])
+      expectedTypes.push(assertedType.expectedType)
+      valid = assertedType.valid
+    }
+  }
+  if (!valid) {
+    if (process.env.NODE_ENV !== 'production') {
+      warn(
+        'Invalid prop: type check failed for prop "' + prop.name + '".' +
+        ' Expected ' + expectedTypes.map(formatType).join(', ') +
+        ', got ' + formatValue(value) + '.',
+        vm
+      )
+    }
+    return false
+  }
+  var validator = options.validator
+  if (validator) {
+    if (!validator(value)) {
+      process.env.NODE_ENV !== 'production' && warn(
+        'Invalid prop: custom validator check failed for prop "' + prop.name + '".',
+        vm
+      )
+      return false
+    }
+  }
+  return true
+}
+
+/**
+ * Force parsing value with coerce option.
+ *
+ * @param {*} value
+ * @param {Object} options
+ * @return {*}
+ */
+
+function coerceProp (prop, value, vm) {
+  var coerce = prop.options.coerce
+  if (!coerce) {
+    return value
+  }
+  if (typeof coerce === 'function') {
+    return coerce(value)
+  } else {
+    process.env.NODE_ENV !== 'production' && warn(
+      'Invalid coerce for prop "' + prop.name + '": expected function, got ' + typeof coerce + '.',
+      vm
+    )
+    return value
+  }
+}
+
+/**
+ * Assert the type of a value
+ *
+ * @param {*} value
+ * @param {Function} type
+ * @return {Object}
+ */
+
+function assertType (value, type) {
+  var valid
+  var expectedType
+  if (type === String) {
+    expectedType = 'string'
+    valid = typeof value === expectedType
+  } else if (type === Number) {
+    expectedType = 'number'
+    valid = typeof value === expectedType
+  } else if (type === Boolean) {
+    expectedType = 'boolean'
+    valid = typeof value === expectedType
+  } else if (type === Function) {
+    expectedType = 'function'
+    valid = typeof value === expectedType
+  } else if (type === Object) {
+    expectedType = 'object'
+    valid = isPlainObject(value)
+  } else if (type === Array) {
+    expectedType = 'array'
+    valid = isArray(value)
+  } else {
+    valid = value instanceof type
+  }
+  return {
+    valid,
+    expectedType
+  }
+}
+
+/**
+ * Format type for output
+ *
+ * @param {String} type
+ * @return {String}
+ */
+
+function formatType (type) {
+  return type
+    ? type.charAt(0).toUpperCase() + type.slice(1)
+    : 'custom type'
+}
+
+/**
+ * Format value
+ *
+ * @param {*} value
+ * @return {String}
+ */
+
+function formatValue (val) {
+  return Object.prototype.toString.call(val).slice(8, -1)
+}
diff --git a/src/compiler/compile.js b/src/compiler/compile.js
new file mode 100644
index 00000000000..33c0ac0150b
--- /dev/null
+++ b/src/compiler/compile.js
@@ -0,0 +1,860 @@
+import publicDirectives from '../directives/public/index'
+import internalDirectives from '../directives/internal/index'
+import { compileProps } from './compile-props'
+import { parseText, tokensToExp } from '../parsers/text'
+import { parseDirective } from '../parsers/directive'
+import { parseTemplate } from '../parsers/template'
+import {
+  _toString,
+  resolveAsset,
+  toArray,
+  warn,
+  remove,
+  replace,
+  commonTagRE,
+  checkComponentAttr,
+  findRef,
+  defineReactive,
+  getAttr
+} from '../util/index'
+
+// special binding prefixes
+const bindRE = /^v-bind:|^:/
+const onRE = /^v-on:|^@/
+const dirAttrRE = /^v-([^:]+)(?:$|:(.*)$)/
+const modifierRE = /\.[^\.]+/g
+const transitionRE = /^(v-bind:|:)?transition$/
+
+// default directive priority
+const DEFAULT_PRIORITY = 1000
+const DEFAULT_TERMINAL_PRIORITY = 2000
+
+/**
+ * Compile a template and return a reusable composite link
+ * function, which recursively contains more link functions
+ * inside. This top level compile function would normally
+ * be called on instance root nodes, but can also be used
+ * for partial compilation if the partial argument is true.
+ *
+ * The returned composite link function, when called, will
+ * return an unlink function that tearsdown all directives
+ * created during the linking phase.
+ *
+ * @param {Element|DocumentFragment} el
+ * @param {Object} options
+ * @param {Boolean} partial
+ * @return {Function}
+ */
+
+export function compile (el, options, partial) {
+  // link function for the node itself.
+  var nodeLinkFn = partial || !options._asComponent
+    ? compileNode(el, options)
+    : null
+  // link function for the childNodes
+  var childLinkFn =
+    !(nodeLinkFn && nodeLinkFn.terminal) &&
+    !isScript(el) &&
+    el.hasChildNodes()
+      ? compileNodeList(el.childNodes, options)
+      : null
+
+  /**
+   * A composite linker function to be called on a already
+   * compiled piece of DOM, which instantiates all directive
+   * instances.
+   *
+   * @param {Vue} vm
+   * @param {Element|DocumentFragment} el
+   * @param {Vue} [host] - host vm of transcluded content
+   * @param {Object} [scope] - v-for scope
+   * @param {Fragment} [frag] - link context fragment
+   * @return {Function|undefined}
+   */
+
+  return function compositeLinkFn (vm, el, host, scope, frag) {
+    // cache childNodes before linking parent, fix #657
+    var childNodes = toArray(el.childNodes)
+    // link
+    var dirs = linkAndCapture(function compositeLinkCapturer () {
+      if (nodeLinkFn) nodeLinkFn(vm, el, host, scope, frag)
+      if (childLinkFn) childLinkFn(vm, childNodes, host, scope, frag)
+    }, vm)
+    return makeUnlinkFn(vm, dirs)
+  }
+}
+
+/**
+ * Apply a linker to a vm/element pair and capture the
+ * directives created during the process.
+ *
+ * @param {Function} linker
+ * @param {Vue} vm
+ */
+
+function linkAndCapture (linker, vm) {
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV === 'production') {
+    // reset directives before every capture in production
+    // mode, so that when unlinking we don't need to splice
+    // them out (which turns out to be a perf hit).
+    // they are kept in development mode because they are
+    // useful for Vue's own tests.
+    vm._directives = []
+  }
+  var originalDirCount = vm._directives.length
+  linker()
+  var dirs = vm._directives.slice(originalDirCount)
+  sortDirectives(dirs)
+  for (var i = 0, l = dirs.length; i < l; i++) {
+    dirs[i]._bind()
+  }
+  return dirs
+}
+
+/**
+ * sort directives by priority (stable sort)
+ *
+ * @param {Array} dirs
+ */
+function sortDirectives (dirs) {
+  if (dirs.length === 0) return
+
+  var groupedMap = {}
+  var i, j, k, l
+  var index = 0
+  var priorities = []
+  for (i = 0, j = dirs.length; i < j; i++) {
+    var dir = dirs[i]
+    var priority = dir.descriptor.def.priority || DEFAULT_PRIORITY
+    var array = groupedMap[priority]
+    if (!array) {
+      array = groupedMap[priority] = []
+      priorities.push(priority)
+    }
+    array.push(dir)
+  }
+
+  priorities.sort(function (a, b) {
+    return a > b ? -1 : a === b ? 0 : 1
+  })
+  for (i = 0, j = priorities.length; i < j; i++) {
+    var group = groupedMap[priorities[i]]
+    for (k = 0, l = group.length; k < l; k++) {
+      dirs[index++] = group[k]
+    }
+  }
+}
+
+/**
+ * Linker functions return an unlink function that
+ * tearsdown all directives instances generated during
+ * the process.
+ *
+ * We create unlink functions with only the necessary
+ * information to avoid retaining additional closures.
+ *
+ * @param {Vue} vm
+ * @param {Array} dirs
+ * @param {Vue} [context]
+ * @param {Array} [contextDirs]
+ * @return {Function}
+ */
+
+function makeUnlinkFn (vm, dirs, context, contextDirs) {
+  function unlink (destroying) {
+    teardownDirs(vm, dirs, destroying)
+    if (context && contextDirs) {
+      teardownDirs(context, contextDirs)
+    }
+  }
+  // expose linked directives
+  unlink.dirs = dirs
+  return unlink
+}
+
+/**
+ * Teardown partial linked directives.
+ *
+ * @param {Vue} vm
+ * @param {Array} dirs
+ * @param {Boolean} destroying
+ */
+
+function teardownDirs (vm, dirs, destroying) {
+  var i = dirs.length
+  while (i--) {
+    dirs[i]._teardown()
+    if (process.env.NODE_ENV !== 'production' && !destroying) {
+      vm._directives.$remove(dirs[i])
+    }
+  }
+}
+
+/**
+ * Compile link props on an instance.
+ *
+ * @param {Vue} vm
+ * @param {Element} el
+ * @param {Object} props
+ * @param {Object} [scope]
+ * @return {Function}
+ */
+
+export function compileAndLinkProps (vm, el, props, scope) {
+  var propsLinkFn = compileProps(el, props, vm)
+  var propDirs = linkAndCapture(function () {
+    propsLinkFn(vm, scope)
+  }, vm)
+  return makeUnlinkFn(vm, propDirs)
+}
+
+/**
+ * Compile the root element of an instance.
+ *
+ * 1. attrs on context container (context scope)
+ * 2. attrs on the component template root node, if
+ *    replace:true (child scope)
+ *
+ * If this is a fragment instance, we only need to compile 1.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @param {Object} contextOptions
+ * @return {Function}
+ */
+
+export function compileRoot (el, options, contextOptions) {
+  var containerAttrs = options._containerAttrs
+  var replacerAttrs = options._replacerAttrs
+  var contextLinkFn, replacerLinkFn
+
+  // only need to compile other attributes for
+  // non-fragment instances
+  if (el.nodeType !== 11) {
+    // for components, container and replacer need to be
+    // compiled separately and linked in different scopes.
+    if (options._asComponent) {
+      // 2. container attributes
+      if (containerAttrs && contextOptions) {
+        contextLinkFn = compileDirectives(containerAttrs, contextOptions)
+      }
+      if (replacerAttrs) {
+        // 3. replacer attributes
+        replacerLinkFn = compileDirectives(replacerAttrs, options)
+      }
+    } else {
+      // non-component, just compile as a normal element.
+      replacerLinkFn = compileDirectives(el.attributes, options)
+    }
+  } else if (process.env.NODE_ENV !== 'production' && containerAttrs) {
+    // warn container directives for fragment instances
+    var names = containerAttrs
+      .filter(function (attr) {
+        // allow vue-loader/vueify scoped css attributes
+        return attr.name.indexOf('_v-') < 0 &&
+          // allow event listeners
+          !onRE.test(attr.name) &&
+          // allow slots
+          attr.name !== 'slot'
+      })
+      .map(function (attr) {
+        return '"' + attr.name + '"'
+      })
+    if (names.length) {
+      var plural = names.length > 1
+
+      var componentName = options.el.tagName.toLowerCase()
+      if (componentName === 'component' && options.name) {
+        componentName += ':' + options.name
+      }
+
+      warn(
+        'Attribute' + (plural ? 's ' : ' ') + names.join(', ') +
+        (plural ? ' are' : ' is') + ' ignored on component ' +
+        '<' + componentName + '> because ' +
+        'the component is a fragment instance: ' +
+        'http://vuejs.org/guide/components.html#Fragment-Instance'
+      )
+    }
+  }
+
+  options._containerAttrs = options._replacerAttrs = null
+  return function rootLinkFn (vm, el, scope) {
+    // link context scope dirs
+    var context = vm._context
+    var contextDirs
+    if (context && contextLinkFn) {
+      contextDirs = linkAndCapture(function () {
+        contextLinkFn(context, el, null, scope)
+      }, context)
+    }
+
+    // link self
+    var selfDirs = linkAndCapture(function () {
+      if (replacerLinkFn) replacerLinkFn(vm, el)
+    }, vm)
+
+    // return the unlink function that tearsdown context
+    // container directives.
+    return makeUnlinkFn(vm, selfDirs, context, contextDirs)
+  }
+}
+
+/**
+ * Compile a node and return a nodeLinkFn based on the
+ * node type.
+ *
+ * @param {Node} node
+ * @param {Object} options
+ * @return {Function|null}
+ */
+
+function compileNode (node, options) {
+  var type = node.nodeType
+  if (type === 1 && !isScript(node)) {
+    return compileElement(node, options)
+  } else if (type === 3 && node.data.trim()) {
+    return compileTextNode(node, options)
+  } else {
+    return null
+  }
+}
+
+/**
+ * Compile an element and return a nodeLinkFn.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Function|null}
+ */
+
+function compileElement (el, options) {
+  // preprocess textareas.
+  // textarea treats its text content as the initial value.
+  // just bind it as an attr directive for value.
+  if (el.tagName === 'TEXTAREA') {
+    // a textarea which has v-pre attr should skip complie.
+    if (getAttr(el, 'v-pre') !== null) {
+      return skip
+    }
+    var tokens = parseText(el.value)
+    if (tokens) {
+      el.setAttribute(':value', tokensToExp(tokens))
+      el.value = ''
+    }
+  }
+  var linkFn
+  var hasAttrs = el.hasAttributes()
+  var attrs = hasAttrs && toArray(el.attributes)
+  // check terminal directives (for & if)
+  if (hasAttrs) {
+    linkFn = checkTerminalDirectives(el, attrs, options)
+  }
+  // check element directives
+  if (!linkFn) {
+    linkFn = checkElementDirectives(el, options)
+  }
+  // check component
+  if (!linkFn) {
+    linkFn = checkComponent(el, options)
+  }
+  // normal directives
+  if (!linkFn && hasAttrs) {
+    linkFn = compileDirectives(attrs, options)
+  }
+  return linkFn
+}
+
+/**
+ * Compile a textNode and return a nodeLinkFn.
+ *
+ * @param {TextNode} node
+ * @param {Object} options
+ * @return {Function|null} textNodeLinkFn
+ */
+
+function compileTextNode (node, options) {
+  // skip marked text nodes
+  if (node._skip) {
+    return removeText
+  }
+
+  var tokens = parseText(node.wholeText)
+  if (!tokens) {
+    return null
+  }
+
+  // mark adjacent text nodes as skipped,
+  // because we are using node.wholeText to compile
+  // all adjacent text nodes together. This fixes
+  // issues in IE where sometimes it splits up a single
+  // text node into multiple ones.
+  var next = node.nextSibling
+  while (next && next.nodeType === 3) {
+    next._skip = true
+    next = next.nextSibling
+  }
+
+  var frag = document.createDocumentFragment()
+  var el, token
+  for (var i = 0, l = tokens.length; i < l; i++) {
+    token = tokens[i]
+    el = token.tag
+      ? processTextToken(token, options)
+      : document.createTextNode(token.value)
+    frag.appendChild(el)
+  }
+  return makeTextNodeLinkFn(tokens, frag, options)
+}
+
+/**
+ * Linker for an skipped text node.
+ *
+ * @param {Vue} vm
+ * @param {Text} node
+ */
+
+function removeText (vm, node) {
+  remove(node)
+}
+
+/**
+ * Process a single text token.
+ *
+ * @param {Object} token
+ * @param {Object} options
+ * @return {Node}
+ */
+
+function processTextToken (token, options) {
+  var el
+  if (token.oneTime) {
+    el = document.createTextNode(token.value)
+  } else {
+    if (token.html) {
+      el = document.createComment('v-html')
+      setTokenType('html')
+    } else {
+      // IE will clean up empty textNodes during
+      // frag.cloneNode(true), so we have to give it
+      // something here...
+      el = document.createTextNode(' ')
+      setTokenType('text')
+    }
+  }
+  function setTokenType (type) {
+    if (token.descriptor) return
+    var parsed = parseDirective(token.value)
+    token.descriptor = {
+      name: type,
+      def: publicDirectives[type],
+      expression: parsed.expression,
+      filters: parsed.filters
+    }
+  }
+  return el
+}
+
+/**
+ * Build a function that processes a textNode.
+ *
+ * @param {Array<Object>} tokens
+ * @param {DocumentFragment} frag
+ */
+
+function makeTextNodeLinkFn (tokens, frag) {
+  return function textNodeLinkFn (vm, el, host, scope) {
+    var fragClone = frag.cloneNode(true)
+    var childNodes = toArray(fragClone.childNodes)
+    var token, value, node
+    for (var i = 0, l = tokens.length; i < l; i++) {
+      token = tokens[i]
+      value = token.value
+      if (token.tag) {
+        node = childNodes[i]
+        if (token.oneTime) {
+          value = (scope || vm).$eval(value)
+          if (token.html) {
+            replace(node, parseTemplate(value, true))
+          } else {
+            node.data = _toString(value)
+          }
+        } else {
+          vm._bindDir(token.descriptor, node, host, scope)
+        }
+      }
+    }
+    replace(el, fragClone)
+  }
+}
+
+/**
+ * Compile a node list and return a childLinkFn.
+ *
+ * @param {NodeList} nodeList
+ * @param {Object} options
+ * @return {Function|undefined}
+ */
+
+function compileNodeList (nodeList, options) {
+  var linkFns = []
+  var nodeLinkFn, childLinkFn, node
+  for (var i = 0, l = nodeList.length; i < l; i++) {
+    node = nodeList[i]
+    nodeLinkFn = compileNode(node, options)
+    childLinkFn =
+      !(nodeLinkFn && nodeLinkFn.terminal) &&
+      node.tagName !== 'SCRIPT' &&
+      node.hasChildNodes()
+        ? compileNodeList(node.childNodes, options)
+        : null
+    linkFns.push(nodeLinkFn, childLinkFn)
+  }
+  return linkFns.length
+    ? makeChildLinkFn(linkFns)
+    : null
+}
+
+/**
+ * Make a child link function for a node's childNodes.
+ *
+ * @param {Array<Function>} linkFns
+ * @return {Function} childLinkFn
+ */
+
+function makeChildLinkFn (linkFns) {
+  return function childLinkFn (vm, nodes, host, scope, frag) {
+    var node, nodeLinkFn, childrenLinkFn
+    for (var i = 0, n = 0, l = linkFns.length; i < l; n++) {
+      node = nodes[n]
+      nodeLinkFn = linkFns[i++]
+      childrenLinkFn = linkFns[i++]
+      // cache childNodes before linking parent, fix #657
+      var childNodes = toArray(node.childNodes)
+      if (nodeLinkFn) {
+        nodeLinkFn(vm, node, host, scope, frag)
+      }
+      if (childrenLinkFn) {
+        childrenLinkFn(vm, childNodes, host, scope, frag)
+      }
+    }
+  }
+}
+
+/**
+ * Check for element directives (custom elements that should
+ * be resovled as terminal directives).
+ *
+ * @param {Element} el
+ * @param {Object} options
+ */
+
+function checkElementDirectives (el, options) {
+  var tag = el.tagName.toLowerCase()
+  if (commonTagRE.test(tag)) {
+    return
+  }
+  var def = resolveAsset(options, 'elementDirectives', tag)
+  if (def) {
+    return makeTerminalNodeLinkFn(el, tag, '', options, def)
+  }
+}
+
+/**
+ * Check if an element is a component. If yes, return
+ * a component link function.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Function|undefined}
+ */
+
+function checkComponent (el, options) {
+  var component = checkComponentAttr(el, options)
+  if (component) {
+    var ref = findRef(el)
+    var descriptor = {
+      name: 'component',
+      ref: ref,
+      expression: component.id,
+      def: internalDirectives.component,
+      modifiers: {
+        literal: !component.dynamic
+      }
+    }
+    var componentLinkFn = function (vm, el, host, scope, frag) {
+      if (ref) {
+        defineReactive((scope || vm).$refs, ref, null)
+      }
+      vm._bindDir(descriptor, el, host, scope, frag)
+    }
+    componentLinkFn.terminal = true
+    return componentLinkFn
+  }
+}
+
+/**
+ * Check an element for terminal directives in fixed order.
+ * If it finds one, return a terminal link function.
+ *
+ * @param {Element} el
+ * @param {Array} attrs
+ * @param {Object} options
+ * @return {Function} terminalLinkFn
+ */
+
+function checkTerminalDirectives (el, attrs, options) {
+  // skip v-pre
+  if (getAttr(el, 'v-pre') !== null) {
+    return skip
+  }
+  // skip v-else block, but only if following v-if
+  if (el.hasAttribute('v-else')) {
+    var prev = el.previousElementSibling
+    if (prev && prev.hasAttribute('v-if')) {
+      return skip
+    }
+  }
+
+  var attr, name, value, modifiers, matched, dirName, rawName, arg, def, termDef
+  for (var i = 0, j = attrs.length; i < j; i++) {
+    attr = attrs[i]
+    name = attr.name.replace(modifierRE, '')
+    if ((matched = name.match(dirAttrRE))) {
+      def = resolveAsset(options, 'directives', matched[1])
+      if (def && def.terminal) {
+        if (!termDef || ((def.priority || DEFAULT_TERMINAL_PRIORITY) > termDef.priority)) {
+          termDef = def
+          rawName = attr.name
+          modifiers = parseModifiers(attr.name)
+          value = attr.value
+          dirName = matched[1]
+          arg = matched[2]
+        }
+      }
+    }
+  }
+
+  if (termDef) {
+    return makeTerminalNodeLinkFn(el, dirName, value, options, termDef, rawName, arg, modifiers)
+  }
+}
+
+function skip () {}
+skip.terminal = true
+
+/**
+ * Build a node link function for a terminal directive.
+ * A terminal link function terminates the current
+ * compilation recursion and handles compilation of the
+ * subtree in the directive.
+ *
+ * @param {Element} el
+ * @param {String} dirName
+ * @param {String} value
+ * @param {Object} options
+ * @param {Object} def
+ * @param {String} [rawName]
+ * @param {String} [arg]
+ * @param {Object} [modifiers]
+ * @return {Function} terminalLinkFn
+ */
+
+function makeTerminalNodeLinkFn (el, dirName, value, options, def, rawName, arg, modifiers) {
+  var parsed = parseDirective(value)
+  var descriptor = {
+    name: dirName,
+    arg: arg,
+    expression: parsed.expression,
+    filters: parsed.filters,
+    raw: value,
+    attr: rawName,
+    modifiers: modifiers,
+    def: def
+  }
+  // check ref for v-for, v-if and router-view
+  if (dirName === 'for' || dirName === 'router-view') {
+    descriptor.ref = findRef(el)
+  }
+  var fn = function terminalNodeLinkFn (vm, el, host, scope, frag) {
+    if (descriptor.ref) {
+      defineReactive((scope || vm).$refs, descriptor.ref, null)
+    }
+    vm._bindDir(descriptor, el, host, scope, frag)
+  }
+  fn.terminal = true
+  return fn
+}
+
+/**
+ * Compile the directives on an element and return a linker.
+ *
+ * @param {Array|NamedNodeMap} attrs
+ * @param {Object} options
+ * @return {Function}
+ */
+
+function compileDirectives (attrs, options) {
+  var i = attrs.length
+  var dirs = []
+  var attr, name, value, rawName, rawValue, dirName, arg, modifiers, dirDef, tokens, matched
+  while (i--) {
+    attr = attrs[i]
+    name = rawName = attr.name
+    value = rawValue = attr.value
+    tokens = parseText(value)
+    // reset arg
+    arg = null
+    // check modifiers
+    modifiers = parseModifiers(name)
+    name = name.replace(modifierRE, '')
+
+    // attribute interpolations
+    if (tokens) {
+      value = tokensToExp(tokens)
+      arg = name
+      pushDir('bind', publicDirectives.bind, tokens)
+      // warn against mixing mustaches with v-bind
+      if (process.env.NODE_ENV !== 'production') {
+        if (name === 'class' && Array.prototype.some.call(attrs, function (attr) {
+          return attr.name === ':class' || attr.name === 'v-bind:class'
+        })) {
+          warn(
+            'class="' + rawValue + '": Do not mix mustache interpolation ' +
+            'and v-bind for "class" on the same element. Use one or the other.',
+            options
+          )
+        }
+      }
+    } else
+
+    // special attribute: transition
+    if (transitionRE.test(name)) {
+      modifiers.literal = !bindRE.test(name)
+      pushDir('transition', internalDirectives.transition)
+    } else
+
+    // event handlers
+    if (onRE.test(name)) {
+      arg = name.replace(onRE, '')
+      pushDir('on', publicDirectives.on)
+    } else
+
+    // attribute bindings
+    if (bindRE.test(name)) {
+      dirName = name.replace(bindRE, '')
+      if (dirName === 'style' || dirName === 'class') {
+        pushDir(dirName, internalDirectives[dirName])
+      } else {
+        arg = dirName
+        pushDir('bind', publicDirectives.bind)
+      }
+    } else
+
+    // normal directives
+    if ((matched = name.match(dirAttrRE))) {
+      dirName = matched[1]
+      arg = matched[2]
+
+      // skip v-else (when used with v-show)
+      if (dirName === 'else') {
+        continue
+      }
+
+      dirDef = resolveAsset(options, 'directives', dirName, true)
+      if (dirDef) {
+        pushDir(dirName, dirDef)
+      }
+    }
+  }
+
+  /**
+   * Push a directive.
+   *
+   * @param {String} dirName
+   * @param {Object|Function} def
+   * @param {Array} [interpTokens]
+   */
+
+  function pushDir (dirName, def, interpTokens) {
+    var hasOneTimeToken = interpTokens && hasOneTime(interpTokens)
+    var parsed = !hasOneTimeToken && parseDirective(value)
+    dirs.push({
+      name: dirName,
+      attr: rawName,
+      raw: rawValue,
+      def: def,
+      arg: arg,
+      modifiers: modifiers,
+      // conversion from interpolation strings with one-time token
+      // to expression is differed until directive bind time so that we
+      // have access to the actual vm context for one-time bindings.
+      expression: parsed && parsed.expression,
+      filters: parsed && parsed.filters,
+      interp: interpTokens,
+      hasOneTime: hasOneTimeToken
+    })
+  }
+
+  if (dirs.length) {
+    return makeNodeLinkFn(dirs)
+  }
+}
+
+/**
+ * Parse modifiers from directive attribute name.
+ *
+ * @param {String} name
+ * @return {Object}
+ */
+
+function parseModifiers (name) {
+  var res = Object.create(null)
+  var match = name.match(modifierRE)
+  if (match) {
+    var i = match.length
+    while (i--) {
+      res[match[i].slice(1)] = true
+    }
+  }
+  return res
+}
+
+/**
+ * Build a link function for all directives on a single node.
+ *
+ * @param {Array} directives
+ * @return {Function} directivesLinkFn
+ */
+
+function makeNodeLinkFn (directives) {
+  return function nodeLinkFn (vm, el, host, scope, frag) {
+    // reverse apply because it's sorted low to high
+    var i = directives.length
+    while (i--) {
+      vm._bindDir(directives[i], el, host, scope, frag)
+    }
+  }
+}
+
+/**
+ * Check if an interpolation string contains one-time tokens.
+ *
+ * @param {Array} tokens
+ * @return {Boolean}
+ */
+
+function hasOneTime (tokens) {
+  var i = tokens.length
+  while (i--) {
+    if (tokens[i].oneTime) return true
+  }
+}
+
+function isScript (el) {
+  return el.tagName === 'SCRIPT' && (
+    !el.hasAttribute('type') ||
+    el.getAttribute('type') === 'text/javascript'
+  )
+}
diff --git a/src/compiler/create-compiler.ts b/src/compiler/create-compiler.ts
deleted file mode 100644
index 79a1a112e9c..00000000000
--- a/src/compiler/create-compiler.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { extend } from 'shared/util'
-import { CompilerOptions, CompiledResult, WarningMessage } from 'types/compiler'
-import { detectErrors } from './error-detector'
-import { createCompileToFunctionFn } from './to-function'
-
-export function createCompilerCreator(baseCompile: Function): Function {
-  return function createCompiler(baseOptions: CompilerOptions) {
-    function compile(
-      template: string,
-      options?: CompilerOptions
-    ): CompiledResult {
-      const finalOptions = Object.create(baseOptions)
-      const errors: WarningMessage[] = []
-      const tips: WarningMessage[] = []
-
-      let warn = (
-        msg: WarningMessage,
-        range: { start: number; end: number },
-        tip: string
-      ) => {
-        ;(tip ? tips : errors).push(msg)
-      }
-
-      if (options) {
-        if (__DEV__ && options.outputSourceRange) {
-          // $flow-disable-line
-          const leadingSpaceLength = template.match(/^\s*/)![0].length
-
-          warn = (
-            msg: WarningMessage | string,
-            range: { start: number; end: number },
-            tip: string
-          ) => {
-            const data: WarningMessage = typeof msg === 'string' ? { msg } : msg
-            if (range) {
-              if (range.start != null) {
-                data.start = range.start + leadingSpaceLength
-              }
-              if (range.end != null) {
-                data.end = range.end + leadingSpaceLength
-              }
-            }
-            ;(tip ? tips : errors).push(data)
-          }
-        }
-        // merge custom modules
-        if (options.modules) {
-          finalOptions.modules = (baseOptions.modules || []).concat(
-            options.modules
-          )
-        }
-        // merge custom directives
-        if (options.directives) {
-          finalOptions.directives = extend(
-            Object.create(baseOptions.directives || null),
-            options.directives
-          )
-        }
-        // copy other options
-        for (const key in options) {
-          if (key !== 'modules' && key !== 'directives') {
-            finalOptions[key] = options[key as keyof CompilerOptions]
-          }
-        }
-      }
-
-      finalOptions.warn = warn
-
-      const compiled = baseCompile(template.trim(), finalOptions)
-      if (__DEV__) {
-        detectErrors(compiled.ast, warn)
-      }
-      compiled.errors = errors
-      compiled.tips = tips
-      return compiled
-    }
-
-    return {
-      compile,
-      compileToFunctions: createCompileToFunctionFn(compile)
-    }
-  }
-}
diff --git a/src/compiler/directives/bind.ts b/src/compiler/directives/bind.ts
deleted file mode 100644
index 173871a4949..00000000000
--- a/src/compiler/directives/bind.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { ASTDirective, ASTElement } from 'types/compiler'
-
-export default function bind(el: ASTElement, dir: ASTDirective) {
-  el.wrapData = (code: string) => {
-    return `_b(${code},'${el.tag}',${dir.value},${
-      dir.modifiers && dir.modifiers.prop ? 'true' : 'false'
-    }${dir.modifiers && dir.modifiers.sync ? ',true' : ''})`
-  }
-}
diff --git a/src/compiler/directives/index.ts b/src/compiler/directives/index.ts
deleted file mode 100644
index 72cfb22ab11..00000000000
--- a/src/compiler/directives/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import on from './on'
-import bind from './bind'
-import { noop } from 'shared/util'
-
-export default {
-  on,
-  bind,
-  cloak: noop
-}
diff --git a/src/compiler/directives/model.ts b/src/compiler/directives/model.ts
deleted file mode 100644
index 4538ccda91f..00000000000
--- a/src/compiler/directives/model.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-import { ASTElement, ASTModifiers } from 'types/compiler'
-
-/**
- * Cross-platform code generation for component v-model
- */
-export function genComponentModel(
-  el: ASTElement,
-  value: string,
-  modifiers: ASTModifiers | null
-): void {
-  const { number, trim } = modifiers || {}
-
-  const baseValueExpression = '$$v'
-  let valueExpression = baseValueExpression
-  if (trim) {
-    valueExpression =
-      `(typeof ${baseValueExpression} === 'string'` +
-      `? ${baseValueExpression}.trim()` +
-      `: ${baseValueExpression})`
-  }
-  if (number) {
-    valueExpression = `_n(${valueExpression})`
-  }
-  const assignment = genAssignmentCode(value, valueExpression)
-
-  el.model = {
-    value: `(${value})`,
-    expression: JSON.stringify(value),
-    callback: `function (${baseValueExpression}) {${assignment}}`
-  }
-}
-
-/**
- * Cross-platform codegen helper for generating v-model value assignment code.
- */
-export function genAssignmentCode(value: string, assignment: string): string {
-  const res = parseModel(value)
-  if (res.key === null) {
-    return `${value}=${assignment}`
-  } else {
-    return `$set(${res.exp}, ${res.key}, ${assignment})`
-  }
-}
-
-/**
- * Parse a v-model expression into a base path and a final key segment.
- * Handles both dot-path and possible square brackets.
- *
- * Possible cases:
- *
- * - test
- * - test[key]
- * - test[test1[key]]
- * - test["a"][key]
- * - xxx.test[a[a].test1[key]]
- * - test.xxx.a["asa"][test1[key]]
- *
- */
-
-let len, str, chr, index, expressionPos, expressionEndPos
-
-type ModelParseResult = {
-  exp: string
-  key: string | null
-}
-
-export function parseModel(val: string): ModelParseResult {
-  // Fix https://github.com/vuejs/vue/pull/7730
-  // allow v-model="obj.val " (trailing whitespace)
-  val = val.trim()
-  len = val.length
-
-  if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) {
-    index = val.lastIndexOf('.')
-    if (index > -1) {
-      return {
-        exp: val.slice(0, index),
-        key: '"' + val.slice(index + 1) + '"'
-      }
-    } else {
-      return {
-        exp: val,
-        key: null
-      }
-    }
-  }
-
-  str = val
-  index = expressionPos = expressionEndPos = 0
-
-  while (!eof()) {
-    chr = next()
-    /* istanbul ignore if */
-    if (isStringStart(chr)) {
-      parseString(chr)
-    } else if (chr === 0x5b) {
-      parseBracket(chr)
-    }
-  }
-
-  return {
-    exp: val.slice(0, expressionPos),
-    key: val.slice(expressionPos + 1, expressionEndPos)
-  }
-}
-
-function next(): number {
-  return str.charCodeAt(++index)
-}
-
-function eof(): boolean {
-  return index >= len
-}
-
-function isStringStart(chr: number): boolean {
-  return chr === 0x22 || chr === 0x27
-}
-
-function parseBracket(chr: number): void {
-  let inBracket = 1
-  expressionPos = index
-  while (!eof()) {
-    chr = next()
-    if (isStringStart(chr)) {
-      parseString(chr)
-      continue
-    }
-    if (chr === 0x5b) inBracket++
-    if (chr === 0x5d) inBracket--
-    if (inBracket === 0) {
-      expressionEndPos = index
-      break
-    }
-  }
-}
-
-function parseString(chr: number): void {
-  const stringQuote = chr
-  while (!eof()) {
-    chr = next()
-    if (chr === stringQuote) {
-      break
-    }
-  }
-}
diff --git a/src/compiler/directives/on.ts b/src/compiler/directives/on.ts
deleted file mode 100644
index 291d4da3a3f..00000000000
--- a/src/compiler/directives/on.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { warn } from 'core/util/index'
-import { ASTDirective, ASTElement } from 'types/compiler'
-
-export default function on(el: ASTElement, dir: ASTDirective) {
-  if (__DEV__ && dir.modifiers) {
-    warn(`v-on without argument does not support modifiers.`)
-  }
-  el.wrapListeners = (code: string) => `_g(${code},${dir.value})`
-}
diff --git a/src/compiler/error-detector.ts b/src/compiler/error-detector.ts
deleted file mode 100644
index d60452f7d99..00000000000
--- a/src/compiler/error-detector.ts
+++ /dev/null
@@ -1,158 +0,0 @@
-import { ASTElement, ASTNode } from 'types/compiler'
-import { dirRE, onRE } from './parser/index'
-
-type Range = { start?: number; end?: number }
-
-// these keywords should not appear inside expressions, but operators like
-// typeof, instanceof and in are allowed
-const prohibitedKeywordRE = new RegExp(
-  '\\b' +
-    (
-      'do,if,for,let,new,try,var,case,else,with,await,break,catch,class,const,' +
-      'super,throw,while,yield,delete,export,import,return,switch,default,' +
-      'extends,finally,continue,debugger,function,arguments'
-    )
-      .split(',')
-      .join('\\b|\\b') +
-    '\\b'
-)
-
-// these unary operators should not be used as property/method names
-const unaryOperatorsRE = new RegExp(
-  '\\b' +
-    'delete,typeof,void'.split(',').join('\\s*\\([^\\)]*\\)|\\b') +
-    '\\s*\\([^\\)]*\\)'
-)
-
-// strip strings in expressions
-const stripStringRE =
-  /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g
-
-// detect problematic expressions in a template
-export function detectErrors(ast: ASTNode | undefined, warn: Function) {
-  if (ast) {
-    checkNode(ast, warn)
-  }
-}
-
-function checkNode(node: ASTNode, warn: Function) {
-  if (node.type === 1) {
-    for (const name in node.attrsMap) {
-      if (dirRE.test(name)) {
-        const value = node.attrsMap[name]
-        if (value) {
-          const range = node.rawAttrsMap[name]
-          if (name === 'v-for') {
-            checkFor(node, `v-for="${value}"`, warn, range)
-          } else if (name === 'v-slot' || name[0] === '#') {
-            checkFunctionParameterExpression(
-              value,
-              `${name}="${value}"`,
-              warn,
-              range
-            )
-          } else if (onRE.test(name)) {
-            checkEvent(value, `${name}="${value}"`, warn, range)
-          } else {
-            checkExpression(value, `${name}="${value}"`, warn, range)
-          }
-        }
-      }
-    }
-    if (node.children) {
-      for (let i = 0; i < node.children.length; i++) {
-        checkNode(node.children[i], warn)
-      }
-    }
-  } else if (node.type === 2) {
-    checkExpression(node.expression, node.text, warn, node)
-  }
-}
-
-function checkEvent(exp: string, text: string, warn: Function, range?: Range) {
-  const stripped = exp.replace(stripStringRE, '')
-  const keywordMatch: any = stripped.match(unaryOperatorsRE)
-  if (keywordMatch && stripped.charAt(keywordMatch.index - 1) !== '$') {
-    warn(
-      `avoid using JavaScript unary operator as property name: ` +
-        `"${keywordMatch[0]}" in expression ${text.trim()}`,
-      range
-    )
-  }
-  checkExpression(exp, text, warn, range)
-}
-
-function checkFor(
-  node: ASTElement,
-  text: string,
-  warn: Function,
-  range?: Range
-) {
-  checkExpression(node.for || '', text, warn, range)
-  checkIdentifier(node.alias, 'v-for alias', text, warn, range)
-  checkIdentifier(node.iterator1, 'v-for iterator', text, warn, range)
-  checkIdentifier(node.iterator2, 'v-for iterator', text, warn, range)
-}
-
-function checkIdentifier(
-  ident: string | null | undefined,
-  type: string,
-  text: string,
-  warn: Function,
-  range?: Range
-) {
-  if (typeof ident === 'string') {
-    try {
-      new Function(`var ${ident}=_`)
-    } catch (e: any) {
-      warn(`invalid ${type} "${ident}" in expression: ${text.trim()}`, range)
-    }
-  }
-}
-
-function checkExpression(
-  exp: string,
-  text: string,
-  warn: Function,
-  range?: Range
-) {
-  try {
-    new Function(`return ${exp}`)
-  } catch (e: any) {
-    const keywordMatch = exp
-      .replace(stripStringRE, '')
-      .match(prohibitedKeywordRE)
-    if (keywordMatch) {
-      warn(
-        `avoid using JavaScript keyword as property name: ` +
-          `"${keywordMatch[0]}"\n  Raw expression: ${text.trim()}`,
-        range
-      )
-    } else {
-      warn(
-        `invalid expression: ${e.message} in\n\n` +
-          `    ${exp}\n\n` +
-          `  Raw expression: ${text.trim()}\n`,
-        range
-      )
-    }
-  }
-}
-
-function checkFunctionParameterExpression(
-  exp: string,
-  text: string,
-  warn: Function,
-  range?: Range
-) {
-  try {
-    new Function(exp, '')
-  } catch (e: any) {
-    warn(
-      `invalid function parameter expression: ${e.message} in\n\n` +
-        `    ${exp}\n\n` +
-        `  Raw expression: ${text.trim()}\n`,
-      range
-    )
-  }
-}
diff --git a/src/compiler/helpers.ts b/src/compiler/helpers.ts
deleted file mode 100644
index f07b28f35c8..00000000000
--- a/src/compiler/helpers.ts
+++ /dev/null
@@ -1,243 +0,0 @@
-import { emptyObject } from 'shared/util'
-import { ASTElement, ASTModifiers } from 'types/compiler'
-import { parseFilters } from './parser/filter-parser'
-
-type Range = { start?: number; end?: number }
-
-/* eslint-disable no-unused-vars */
-export function baseWarn(msg: string, range?: Range) {
-  console.error(`[Vue compiler]: ${msg}`)
-}
-/* eslint-enable no-unused-vars */
-
-export function pluckModuleFunction<T, K extends keyof T>(
-  modules: Array<T> | undefined,
-  key: K
-): Array<Exclude<T[K], undefined>> {
-  return modules ? (modules.map(m => m[key]).filter(_ => _) as any) : []
-}
-
-export function addProp(
-  el: ASTElement,
-  name: string,
-  value: string,
-  range?: Range,
-  dynamic?: boolean
-) {
-  ;(el.props || (el.props = [])).push(
-    rangeSetItem({ name, value, dynamic }, range)
-  )
-  el.plain = false
-}
-
-export function addAttr(
-  el: ASTElement,
-  name: string,
-  value: any,
-  range?: Range,
-  dynamic?: boolean
-) {
-  const attrs = dynamic
-    ? el.dynamicAttrs || (el.dynamicAttrs = [])
-    : el.attrs || (el.attrs = [])
-  attrs.push(rangeSetItem({ name, value, dynamic }, range))
-  el.plain = false
-}
-
-// add a raw attr (use this in preTransforms)
-export function addRawAttr(
-  el: ASTElement,
-  name: string,
-  value: any,
-  range?: Range
-) {
-  el.attrsMap[name] = value
-  el.attrsList.push(rangeSetItem({ name, value }, range))
-}
-
-export function addDirective(
-  el: ASTElement,
-  name: string,
-  rawName: string,
-  value: string,
-  arg?: string,
-  isDynamicArg?: boolean,
-  modifiers?: ASTModifiers,
-  range?: Range
-) {
-  ;(el.directives || (el.directives = [])).push(
-    rangeSetItem(
-      {
-        name,
-        rawName,
-        value,
-        arg,
-        isDynamicArg,
-        modifiers
-      },
-      range
-    )
-  )
-  el.plain = false
-}
-
-function prependModifierMarker(
-  symbol: string,
-  name: string,
-  dynamic?: boolean
-): string {
-  return dynamic ? `_p(${name},"${symbol}")` : symbol + name // mark the event as captured
-}
-
-export function addHandler(
-  el: ASTElement,
-  name: string,
-  value: string,
-  modifiers?: ASTModifiers | null,
-  important?: boolean,
-  warn?: Function,
-  range?: Range,
-  dynamic?: boolean
-) {
-  modifiers = modifiers || emptyObject
-  // warn prevent and passive modifier
-  /* istanbul ignore if */
-  if (__DEV__ && warn && modifiers.prevent && modifiers.passive) {
-    warn(
-      "passive and prevent can't be used together. " +
-        "Passive handler can't prevent default event.",
-      range
-    )
-  }
-
-  // normalize click.right and click.middle since they don't actually fire
-  // this is technically browser-specific, but at least for now browsers are
-  // the only target envs that have right/middle clicks.
-  if (modifiers.right) {
-    if (dynamic) {
-      name = `(${name})==='click'?'contextmenu':(${name})`
-    } else if (name === 'click') {
-      name = 'contextmenu'
-      delete modifiers.right
-    }
-  } else if (modifiers.middle) {
-    if (dynamic) {
-      name = `(${name})==='click'?'mouseup':(${name})`
-    } else if (name === 'click') {
-      name = 'mouseup'
-    }
-  }
-
-  // check capture modifier
-  if (modifiers.capture) {
-    delete modifiers.capture
-    name = prependModifierMarker('!', name, dynamic)
-  }
-  if (modifiers.once) {
-    delete modifiers.once
-    name = prependModifierMarker('~', name, dynamic)
-  }
-  /* istanbul ignore if */
-  if (modifiers.passive) {
-    delete modifiers.passive
-    name = prependModifierMarker('&', name, dynamic)
-  }
-
-  let events
-  if (modifiers.native) {
-    delete modifiers.native
-    events = el.nativeEvents || (el.nativeEvents = {})
-  } else {
-    events = el.events || (el.events = {})
-  }
-
-  const newHandler: any = rangeSetItem({ value: value.trim(), dynamic }, range)
-  if (modifiers !== emptyObject) {
-    newHandler.modifiers = modifiers
-  }
-
-  const handlers = events[name]
-  /* istanbul ignore if */
-  if (Array.isArray(handlers)) {
-    important ? handlers.unshift(newHandler) : handlers.push(newHandler)
-  } else if (handlers) {
-    events[name] = important ? [newHandler, handlers] : [handlers, newHandler]
-  } else {
-    events[name] = newHandler
-  }
-
-  el.plain = false
-}
-
-export function getRawBindingAttr(el: ASTElement, name: string) {
-  return (
-    el.rawAttrsMap[':' + name] ||
-    el.rawAttrsMap['v-bind:' + name] ||
-    el.rawAttrsMap[name]
-  )
-}
-
-export function getBindingAttr(
-  el: ASTElement,
-  name: string,
-  getStatic?: boolean
-): string | undefined {
-  const dynamicValue =
-    getAndRemoveAttr(el, ':' + name) || getAndRemoveAttr(el, 'v-bind:' + name)
-  if (dynamicValue != null) {
-    return parseFilters(dynamicValue)
-  } else if (getStatic !== false) {
-    const staticValue = getAndRemoveAttr(el, name)
-    if (staticValue != null) {
-      return JSON.stringify(staticValue)
-    }
-  }
-}
-
-// note: this only removes the attr from the Array (attrsList) so that it
-// doesn't get processed by processAttrs.
-// By default it does NOT remove it from the map (attrsMap) because the map is
-// needed during codegen.
-export function getAndRemoveAttr(
-  el: ASTElement,
-  name: string,
-  removeFromMap?: boolean
-): string | undefined {
-  let val
-  if ((val = el.attrsMap[name]) != null) {
-    const list = el.attrsList
-    for (let i = 0, l = list.length; i < l; i++) {
-      if (list[i].name === name) {
-        list.splice(i, 1)
-        break
-      }
-    }
-  }
-  if (removeFromMap) {
-    delete el.attrsMap[name]
-  }
-  return val
-}
-
-export function getAndRemoveAttrByRegex(el: ASTElement, name: RegExp) {
-  const list = el.attrsList
-  for (let i = 0, l = list.length; i < l; i++) {
-    const attr = list[i]
-    if (name.test(attr.name)) {
-      list.splice(i, 1)
-      return attr
-    }
-  }
-}
-
-function rangeSetItem(item: any, range?: { start?: number; end?: number }) {
-  if (range) {
-    if (range.start != null) {
-      item.start = range.start
-    }
-    if (range.end != null) {
-      item.end = range.end
-    }
-  }
-  return item
-}
diff --git a/src/compiler/index.js b/src/compiler/index.js
new file mode 100644
index 00000000000..274078829f8
--- /dev/null
+++ b/src/compiler/index.js
@@ -0,0 +1,3 @@
+export * from './compile'
+export * from './transclude'
+export * from './resolve-slots'
diff --git a/src/compiler/index.ts b/src/compiler/index.ts
deleted file mode 100644
index e8a17e11dc4..00000000000
--- a/src/compiler/index.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { parse } from './parser/index'
-import { optimize } from './optimizer'
-import { generate } from './codegen/index'
-import { createCompilerCreator } from './create-compiler'
-import { CompilerOptions, CompiledResult } from 'types/compiler'
-
-// `createCompilerCreator` allows creating compilers that use alternative
-// parser/optimizer/codegen, e.g the SSR optimizing compiler.
-// Here we just export a default compiler using the default parts.
-export const createCompiler = createCompilerCreator(function baseCompile(
-  template: string,
-  options: CompilerOptions
-): CompiledResult {
-  const ast = parse(template.trim(), options)
-  if (options.optimize !== false) {
-    optimize(ast, options)
-  }
-  const code = generate(ast, options)
-  return {
-    ast,
-    render: code.render,
-    staticRenderFns: code.staticRenderFns
-  }
-})
diff --git a/src/compiler/optimizer.ts b/src/compiler/optimizer.ts
deleted file mode 100644
index 49ef6aee2f1..00000000000
--- a/src/compiler/optimizer.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-import { makeMap, isBuiltInTag, cached, no } from 'shared/util'
-import { ASTElement, CompilerOptions, ASTNode } from 'types/compiler'
-
-let isStaticKey
-let isPlatformReservedTag
-
-const genStaticKeysCached = cached(genStaticKeys)
-
-/**
- * Goal of the optimizer: walk the generated template AST tree
- * and detect sub-trees that are purely static, i.e. parts of
- * the DOM that never needs to change.
- *
- * Once we detect these sub-trees, we can:
- *
- * 1. Hoist them into constants, so that we no longer need to
- *    create fresh nodes for them on each re-render;
- * 2. Completely skip them in the patching process.
- */
-export function optimize(
-  root: ASTElement | null | undefined,
-  options: CompilerOptions
-) {
-  if (!root) return
-  isStaticKey = genStaticKeysCached(options.staticKeys || '')
-  isPlatformReservedTag = options.isReservedTag || no
-  // first pass: mark all non-static nodes.
-  markStatic(root)
-  // second pass: mark static roots.
-  markStaticRoots(root, false)
-}
-
-function genStaticKeys(keys: string): Function {
-  return makeMap(
-    'type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap' +
-      (keys ? ',' + keys : '')
-  )
-}
-
-function markStatic(node: ASTNode) {
-  node.static = isStatic(node)
-  if (node.type === 1) {
-    // do not make component slot content static. this avoids
-    // 1. components not able to mutate slot nodes
-    // 2. static slot content fails for hot-reloading
-    if (
-      !isPlatformReservedTag(node.tag) &&
-      node.tag !== 'slot' &&
-      node.attrsMap['inline-template'] == null
-    ) {
-      return
-    }
-    for (let i = 0, l = node.children.length; i < l; i++) {
-      const child = node.children[i]
-      markStatic(child)
-      if (!child.static) {
-        node.static = false
-      }
-    }
-    if (node.ifConditions) {
-      for (let i = 1, l = node.ifConditions.length; i < l; i++) {
-        const block = node.ifConditions[i].block
-        markStatic(block)
-        if (!block.static) {
-          node.static = false
-        }
-      }
-    }
-  }
-}
-
-function markStaticRoots(node: ASTNode, isInFor: boolean) {
-  if (node.type === 1) {
-    if (node.static || node.once) {
-      node.staticInFor = isInFor
-    }
-    // For a node to qualify as a static root, it should have children that
-    // are not just static text. Otherwise the cost of hoisting out will
-    // outweigh the benefits and it's better off to just always render it fresh.
-    if (
-      node.static &&
-      node.children.length &&
-      !(node.children.length === 1 && node.children[0].type === 3)
-    ) {
-      node.staticRoot = true
-      return
-    } else {
-      node.staticRoot = false
-    }
-    if (node.children) {
-      for (let i = 0, l = node.children.length; i < l; i++) {
-        markStaticRoots(node.children[i], isInFor || !!node.for)
-      }
-    }
-    if (node.ifConditions) {
-      for (let i = 1, l = node.ifConditions.length; i < l; i++) {
-        markStaticRoots(node.ifConditions[i].block, isInFor)
-      }
-    }
-  }
-}
-
-function isStatic(node: ASTNode): boolean {
-  if (node.type === 2) {
-    // expression
-    return false
-  }
-  if (node.type === 3) {
-    // text
-    return true
-  }
-  return !!(
-    node.pre ||
-    (!node.hasBindings && // no dynamic bindings
-      !node.if &&
-      !node.for && // not v-if or v-for or v-else
-      !isBuiltInTag(node.tag) && // not a built-in
-      isPlatformReservedTag(node.tag) && // not a component
-      !isDirectChildOfTemplateFor(node) &&
-      Object.keys(node).every(isStaticKey))
-  )
-}
-
-function isDirectChildOfTemplateFor(node: ASTElement): boolean {
-  while (node.parent) {
-    node = node.parent
-    if (node.tag !== 'template') {
-      return false
-    }
-    if (node.for) {
-      return true
-    }
-  }
-  return false
-}
diff --git a/src/compiler/parser/entity-decoder.ts b/src/compiler/parser/entity-decoder.ts
deleted file mode 100644
index 031f3291763..00000000000
--- a/src/compiler/parser/entity-decoder.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-let decoder
-
-export default {
-  decode(html: string): string {
-    decoder = decoder || document.createElement('div')
-    decoder.innerHTML = html
-    return decoder.textContent
-  }
-}
diff --git a/src/compiler/parser/filter-parser.ts b/src/compiler/parser/filter-parser.ts
deleted file mode 100644
index b5aa5b7f4a6..00000000000
--- a/src/compiler/parser/filter-parser.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-const validDivisionCharRE = /[\w).+\-_$\]]/
-
-export function parseFilters(exp: string): string {
-  let inSingle = false
-  let inDouble = false
-  let inTemplateString = false
-  let inRegex = false
-  let curly = 0
-  let square = 0
-  let paren = 0
-  let lastFilterIndex = 0
-  let c, prev, i, expression, filters
-
-  for (i = 0; i < exp.length; i++) {
-    prev = c
-    c = exp.charCodeAt(i)
-    if (inSingle) {
-      if (c === 0x27 && prev !== 0x5c) inSingle = false
-    } else if (inDouble) {
-      if (c === 0x22 && prev !== 0x5c) inDouble = false
-    } else if (inTemplateString) {
-      if (c === 0x60 && prev !== 0x5c) inTemplateString = false
-    } else if (inRegex) {
-      if (c === 0x2f && prev !== 0x5c) inRegex = false
-    } else if (
-      c === 0x7c && // pipe
-      exp.charCodeAt(i + 1) !== 0x7c &&
-      exp.charCodeAt(i - 1) !== 0x7c &&
-      !curly &&
-      !square &&
-      !paren
-    ) {
-      if (expression === undefined) {
-        // first filter, end of expression
-        lastFilterIndex = i + 1
-        expression = exp.slice(0, i).trim()
-      } else {
-        pushFilter()
-      }
-    } else {
-      switch (c) {
-        case 0x22:
-          inDouble = true
-          break // "
-        case 0x27:
-          inSingle = true
-          break // '
-        case 0x60:
-          inTemplateString = true
-          break // `
-        case 0x28:
-          paren++
-          break // (
-        case 0x29:
-          paren--
-          break // )
-        case 0x5b:
-          square++
-          break // [
-        case 0x5d:
-          square--
-          break // ]
-        case 0x7b:
-          curly++
-          break // {
-        case 0x7d:
-          curly--
-          break // }
-      }
-      if (c === 0x2f) {
-        // /
-        let j = i - 1
-        let p
-        // find first non-whitespace prev char
-        for (; j >= 0; j--) {
-          p = exp.charAt(j)
-          if (p !== ' ') break
-        }
-        if (!p || !validDivisionCharRE.test(p)) {
-          inRegex = true
-        }
-      }
-    }
-  }
-
-  if (expression === undefined) {
-    expression = exp.slice(0, i).trim()
-  } else if (lastFilterIndex !== 0) {
-    pushFilter()
-  }
-
-  function pushFilter() {
-    ;(filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim())
-    lastFilterIndex = i + 1
-  }
-
-  if (filters) {
-    for (i = 0; i < filters.length; i++) {
-      expression = wrapFilter(expression, filters[i])
-    }
-  }
-
-  return expression
-}
-
-function wrapFilter(exp: string, filter: string): string {
-  const i = filter.indexOf('(')
-  if (i < 0) {
-    // _f: resolveFilter
-    return `_f("${filter}")(${exp})`
-  } else {
-    const name = filter.slice(0, i)
-    const args = filter.slice(i + 1)
-    return `_f("${name}")(${exp}${args !== ')' ? ',' + args : args}`
-  }
-}
diff --git a/src/compiler/parser/html-parser.ts b/src/compiler/parser/html-parser.ts
deleted file mode 100644
index 29acbfc8fc5..00000000000
--- a/src/compiler/parser/html-parser.ts
+++ /dev/null
@@ -1,341 +0,0 @@
-/**
- * Not type-checking this file because it's mostly vendor code.
- */
-
-/*!
- * HTML Parser By John Resig (ejohn.org)
- * Modified by Juriy "kangax" Zaytsev
- * Original code by Erik Arvidsson (MPL-1.1 OR Apache-2.0 OR GPL-2.0-or-later)
- * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
- */
-
-import { makeMap, no } from 'shared/util'
-import { isNonPhrasingTag } from 'web/compiler/util'
-import { unicodeRegExp } from 'core/util/lang'
-import { ASTAttr, CompilerOptions } from 'types/compiler'
-
-// Regular Expressions for parsing tags and attributes
-const attribute =
-  /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
-const dynamicArgAttribute =
-  /^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
-const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeRegExp.source}]*`
-const qnameCapture = `((?:${ncname}\\:)?${ncname})`
-const startTagOpen = new RegExp(`^<${qnameCapture}`)
-const startTagClose = /^\s*(\/?)>/
-const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
-const doctype = /^<!DOCTYPE [^>]+>/i
-// #7298: escape - to avoid being passed as HTML comment when inlined in page
-const comment = /^<!\--/
-const conditionalComment = /^<!\[/
-
-// Special Elements (can contain anything)
-export const isPlainTextElement = makeMap('script,style,textarea', true)
-const reCache = {}
-
-const decodingMap = {
-  '&lt;': '<',
-  '&gt;': '>',
-  '&quot;': '"',
-  '&amp;': '&',
-  '&#10;': '\n',
-  '&#9;': '\t',
-  '&#39;': "'"
-}
-const encodedAttr = /&(?:lt|gt|quot|amp|#39);/g
-const encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#39|#10|#9);/g
-
-// #5992
-const isIgnoreNewlineTag = makeMap('pre,textarea', true)
-const shouldIgnoreFirstNewline = (tag, html) =>
-  tag && isIgnoreNewlineTag(tag) && html[0] === '\n'
-
-function decodeAttr(value, shouldDecodeNewlines) {
-  const re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr
-  return value.replace(re, match => decodingMap[match])
-}
-
-export interface HTMLParserOptions extends CompilerOptions {
-  start?: (
-    tag: string,
-    attrs: ASTAttr[],
-    unary: boolean,
-    start: number,
-    end: number
-  ) => void
-  end?: (tag: string, start: number, end: number) => void
-  chars?: (text: string, start?: number, end?: number) => void
-  comment?: (content: string, start: number, end: number) => void
-}
-
-export function parseHTML(html, options: HTMLParserOptions) {
-  const stack: any[] = []
-  const expectHTML = options.expectHTML
-  const isUnaryTag = options.isUnaryTag || no
-  const canBeLeftOpenTag = options.canBeLeftOpenTag || no
-  let index = 0
-  let last, lastTag
-  while (html) {
-    last = html
-    // Make sure we're not in a plaintext content element like script/style
-    if (!lastTag || !isPlainTextElement(lastTag)) {
-      let textEnd = html.indexOf('<')
-      if (textEnd === 0) {
-        // Comment:
-        if (comment.test(html)) {
-          const commentEnd = html.indexOf('-->')
-
-          if (commentEnd >= 0) {
-            if (options.shouldKeepComment && options.comment) {
-              options.comment(
-                html.substring(4, commentEnd),
-                index,
-                index + commentEnd + 3
-              )
-            }
-            advance(commentEnd + 3)
-            continue
-          }
-        }
-
-        // https://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
-        if (conditionalComment.test(html)) {
-          const conditionalEnd = html.indexOf(']>')
-
-          if (conditionalEnd >= 0) {
-            advance(conditionalEnd + 2)
-            continue
-          }
-        }
-
-        // Doctype:
-        const doctypeMatch = html.match(doctype)
-        if (doctypeMatch) {
-          advance(doctypeMatch[0].length)
-          continue
-        }
-
-        // End tag:
-        const endTagMatch = html.match(endTag)
-        if (endTagMatch) {
-          const curIndex = index
-          advance(endTagMatch[0].length)
-          parseEndTag(endTagMatch[1], curIndex, index)
-          continue
-        }
-
-        // Start tag:
-        const startTagMatch = parseStartTag()
-        if (startTagMatch) {
-          handleStartTag(startTagMatch)
-          if (shouldIgnoreFirstNewline(startTagMatch.tagName, html)) {
-            advance(1)
-          }
-          continue
-        }
-      }
-
-      let text, rest, next
-      if (textEnd >= 0) {
-        rest = html.slice(textEnd)
-        while (
-          !endTag.test(rest) &&
-          !startTagOpen.test(rest) &&
-          !comment.test(rest) &&
-          !conditionalComment.test(rest)
-        ) {
-          // < in plain text, be forgiving and treat it as text
-          next = rest.indexOf('<', 1)
-          if (next < 0) break
-          textEnd += next
-          rest = html.slice(textEnd)
-        }
-        text = html.substring(0, textEnd)
-      }
-
-      if (textEnd < 0) {
-        text = html
-      }
-
-      if (text) {
-        advance(text.length)
-      }
-
-      if (options.chars && text) {
-        options.chars(text, index - text.length, index)
-      }
-    } else {
-      let endTagLength = 0
-      const stackedTag = lastTag.toLowerCase()
-      const reStackedTag =
-        reCache[stackedTag] ||
-        (reCache[stackedTag] = new RegExp(
-          '([\\s\\S]*?)(</' + stackedTag + '[^>]*>)',
-          'i'
-        ))
-      const rest = html.replace(reStackedTag, function (all, text, endTag) {
-        endTagLength = endTag.length
-        if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') {
-          text = text
-            .replace(/<!\--([\s\S]*?)-->/g, '$1') // #7298
-            .replace(/<!\[CDATA\[([\s\S]*?)]]>/g, '$1')
-        }
-        if (shouldIgnoreFirstNewline(stackedTag, text)) {
-          text = text.slice(1)
-        }
-        if (options.chars) {
-          options.chars(text)
-        }
-        return ''
-      })
-      index += html.length - rest.length
-      html = rest
-      parseEndTag(stackedTag, index - endTagLength, index)
-    }
-
-    if (html === last) {
-      options.chars && options.chars(html)
-      if (__DEV__ && !stack.length && options.warn) {
-        options.warn(`Mal-formatted tag at end of template: "${html}"`, {
-          start: index + html.length
-        })
-      }
-      break
-    }
-  }
-
-  // Clean up any remaining tags
-  parseEndTag()
-
-  function advance(n) {
-    index += n
-    html = html.substring(n)
-  }
-
-  function parseStartTag() {
-    const start = html.match(startTagOpen)
-    if (start) {
-      const match: any = {
-        tagName: start[1],
-        attrs: [],
-        start: index
-      }
-      advance(start[0].length)
-      let end, attr
-      while (
-        !(end = html.match(startTagClose)) &&
-        (attr = html.match(dynamicArgAttribute) || html.match(attribute))
-      ) {
-        attr.start = index
-        advance(attr[0].length)
-        attr.end = index
-        match.attrs.push(attr)
-      }
-      if (end) {
-        match.unarySlash = end[1]
-        advance(end[0].length)
-        match.end = index
-        return match
-      }
-    }
-  }
-
-  function handleStartTag(match) {
-    const tagName = match.tagName
-    const unarySlash = match.unarySlash
-
-    if (expectHTML) {
-      if (lastTag === 'p' && isNonPhrasingTag(tagName)) {
-        parseEndTag(lastTag)
-      }
-      if (canBeLeftOpenTag(tagName) && lastTag === tagName) {
-        parseEndTag(tagName)
-      }
-    }
-
-    const unary = isUnaryTag(tagName) || !!unarySlash
-
-    const l = match.attrs.length
-    const attrs: ASTAttr[] = new Array(l)
-    for (let i = 0; i < l; i++) {
-      const args = match.attrs[i]
-      const value = args[3] || args[4] || args[5] || ''
-      const shouldDecodeNewlines =
-        tagName === 'a' && args[1] === 'href'
-          ? options.shouldDecodeNewlinesForHref
-          : options.shouldDecodeNewlines
-      attrs[i] = {
-        name: args[1],
-        value: decodeAttr(value, shouldDecodeNewlines)
-      }
-      if (__DEV__ && options.outputSourceRange) {
-        attrs[i].start = args.start + args[0].match(/^\s*/).length
-        attrs[i].end = args.end
-      }
-    }
-
-    if (!unary) {
-      stack.push({
-        tag: tagName,
-        lowerCasedTag: tagName.toLowerCase(),
-        attrs: attrs,
-        start: match.start,
-        end: match.end
-      })
-      lastTag = tagName
-    }
-
-    if (options.start) {
-      options.start(tagName, attrs, unary, match.start, match.end)
-    }
-  }
-
-  function parseEndTag(tagName?: any, start?: any, end?: any) {
-    let pos, lowerCasedTagName
-    if (start == null) start = index
-    if (end == null) end = index
-
-    // Find the closest opened tag of the same type
-    if (tagName) {
-      lowerCasedTagName = tagName.toLowerCase()
-      for (pos = stack.length - 1; pos >= 0; pos--) {
-        if (stack[pos].lowerCasedTag === lowerCasedTagName) {
-          break
-        }
-      }
-    } else {
-      // If no tag name is provided, clean shop
-      pos = 0
-    }
-
-    if (pos >= 0) {
-      // Close all the open elements, up the stack
-      for (let i = stack.length - 1; i >= pos; i--) {
-        if (__DEV__ && (i > pos || !tagName) && options.warn) {
-          options.warn(`tag <${stack[i].tag}> has no matching end tag.`, {
-            start: stack[i].start,
-            end: stack[i].end
-          })
-        }
-        if (options.end) {
-          options.end(stack[i].tag, start, end)
-        }
-      }
-
-      // Remove the open elements from the stack
-      stack.length = pos
-      lastTag = pos && stack[pos - 1].tag
-    } else if (lowerCasedTagName === 'br') {
-      if (options.start) {
-        options.start(tagName, [], true, start, end)
-      }
-    } else if (lowerCasedTagName === 'p') {
-      if (options.start) {
-        options.start(tagName, [], false, start, end)
-      }
-      if (options.end) {
-        options.end(tagName, start, end)
-      }
-    }
-  }
-}
diff --git a/src/compiler/parser/index.ts b/src/compiler/parser/index.ts
deleted file mode 100644
index 148cc0110f5..00000000000
--- a/src/compiler/parser/index.ts
+++ /dev/null
@@ -1,999 +0,0 @@
-import he from 'he'
-import { parseHTML } from './html-parser'
-import { parseText } from './text-parser'
-import { parseFilters } from './filter-parser'
-import { genAssignmentCode } from '../directives/model'
-import { extend, cached, no, camelize, hyphenate } from 'shared/util'
-import { isIE, isEdge, isServerRendering } from 'core/util/env'
-
-import {
-  addProp,
-  addAttr,
-  baseWarn,
-  addHandler,
-  addDirective,
-  getBindingAttr,
-  getAndRemoveAttr,
-  getRawBindingAttr,
-  pluckModuleFunction,
-  getAndRemoveAttrByRegex
-} from '../helpers'
-
-import {
-  ASTAttr,
-  ASTElement,
-  ASTIfCondition,
-  ASTNode,
-  ASTText,
-  CompilerOptions
-} from 'types/compiler'
-
-export const onRE = /^@|^v-on:/
-export const dirRE = process.env.VBIND_PROP_SHORTHAND
-  ? /^v-|^@|^:|^\.|^#/
-  : /^v-|^@|^:|^#/
-export const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
-export const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
-const stripParensRE = /^\(|\)$/g
-const dynamicArgRE = /^\[.*\]$/
-
-const argRE = /:(.*)$/
-export const bindRE = /^:|^\.|^v-bind:/
-const propBindRE = /^\./
-const modifierRE = /\.[^.\]]+(?=[^\]]*$)/g
-
-export const slotRE = /^v-slot(:|$)|^#/
-
-const lineBreakRE = /[\r\n]/
-const whitespaceRE = /[ \f\t\r\n]+/g
-
-const invalidAttributeRE = /[\s"'<>\/=]/
-
-const decodeHTMLCached = cached(he.decode)
-
-export const emptySlotScopeToken = `_empty_`
-
-// configurable state
-export let warn: any
-let delimiters
-let transforms
-let preTransforms
-let postTransforms
-let platformIsPreTag
-let platformMustUseProp
-let platformGetTagNamespace
-let maybeComponent
-
-export function createASTElement(
-  tag: string,
-  attrs: Array<ASTAttr>,
-  parent: ASTElement | void
-): ASTElement {
-  return {
-    type: 1,
-    tag,
-    attrsList: attrs,
-    attrsMap: makeAttrsMap(attrs),
-    rawAttrsMap: {},
-    parent,
-    children: []
-  }
-}
-
-/**
- * Convert HTML string to AST.
- */
-export function parse(template: string, options: CompilerOptions): ASTElement {
-  warn = options.warn || baseWarn
-
-  platformIsPreTag = options.isPreTag || no
-  platformMustUseProp = options.mustUseProp || no
-  platformGetTagNamespace = options.getTagNamespace || no
-  const isReservedTag = options.isReservedTag || no
-  maybeComponent = (el: ASTElement) =>
-    !!(
-      el.component ||
-      el.attrsMap[':is'] ||
-      el.attrsMap['v-bind:is'] ||
-      !(el.attrsMap.is ? isReservedTag(el.attrsMap.is) : isReservedTag(el.tag))
-    )
-  transforms = pluckModuleFunction(options.modules, 'transformNode')
-  preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
-  postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')
-
-  delimiters = options.delimiters
-
-  const stack: any[] = []
-  const preserveWhitespace = options.preserveWhitespace !== false
-  const whitespaceOption = options.whitespace
-  let root
-  let currentParent
-  let inVPre = false
-  let inPre = false
-  let warned = false
-
-  function warnOnce(msg, range) {
-    if (!warned) {
-      warned = true
-      warn(msg, range)
-    }
-  }
-
-  function closeElement(element) {
-    trimEndingWhitespace(element)
-    if (!inVPre && !element.processed) {
-      element = processElement(element, options)
-    }
-    // tree management
-    if (!stack.length && element !== root) {
-      // allow root elements with v-if, v-else-if and v-else
-      if (root.if && (element.elseif || element.else)) {
-        if (__DEV__) {
-          checkRootConstraints(element)
-        }
-        addIfCondition(root, {
-          exp: element.elseif,
-          block: element
-        })
-      } else if (__DEV__) {
-        warnOnce(
-          `Component template should contain exactly one root element. ` +
-            `If you are using v-if on multiple elements, ` +
-            `use v-else-if to chain them instead.`,
-          { start: element.start }
-        )
-      }
-    }
-    if (currentParent && !element.forbidden) {
-      if (element.elseif || element.else) {
-        processIfConditions(element, currentParent)
-      } else {
-        if (element.slotScope) {
-          // scoped slot
-          // keep it in the children list so that v-else(-if) conditions can
-          // find it as the prev node.
-          const name = element.slotTarget || '"default"'
-          ;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[
-            name
-          ] = element
-        }
-        currentParent.children.push(element)
-        element.parent = currentParent
-      }
-    }
-
-    // final children cleanup
-    // filter out scoped slots
-    element.children = element.children.filter(c => !c.slotScope)
-    // remove trailing whitespace node again
-    trimEndingWhitespace(element)
-
-    // check pre state
-    if (element.pre) {
-      inVPre = false
-    }
-    if (platformIsPreTag(element.tag)) {
-      inPre = false
-    }
-    // apply post-transforms
-    for (let i = 0; i < postTransforms.length; i++) {
-      postTransforms[i](element, options)
-    }
-  }
-
-  function trimEndingWhitespace(el) {
-    // remove trailing whitespace node
-    if (!inPre) {
-      let lastNode
-      while (
-        (lastNode = el.children[el.children.length - 1]) &&
-        lastNode.type === 3 &&
-        lastNode.text === ' '
-      ) {
-        el.children.pop()
-      }
-    }
-  }
-
-  function checkRootConstraints(el) {
-    if (el.tag === 'slot' || el.tag === 'template') {
-      warnOnce(
-        `Cannot use <${el.tag}> as component root element because it may ` +
-          'contain multiple nodes.',
-        { start: el.start }
-      )
-    }
-    if (el.attrsMap.hasOwnProperty('v-for')) {
-      warnOnce(
-        'Cannot use v-for on stateful component root element because ' +
-          'it renders multiple elements.',
-        el.rawAttrsMap['v-for']
-      )
-    }
-  }
-
-  parseHTML(template, {
-    warn,
-    expectHTML: options.expectHTML,
-    isUnaryTag: options.isUnaryTag,
-    canBeLeftOpenTag: options.canBeLeftOpenTag,
-    shouldDecodeNewlines: options.shouldDecodeNewlines,
-    shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
-    shouldKeepComment: options.comments,
-    outputSourceRange: options.outputSourceRange,
-    start(tag, attrs, unary, start, end) {
-      // check namespace.
-      // inherit parent ns if there is one
-      const ns =
-        (currentParent && currentParent.ns) || platformGetTagNamespace(tag)
-
-      // handle IE svg bug
-      /* istanbul ignore if */
-      if (isIE && ns === 'svg') {
-        attrs = guardIESVGBug(attrs)
-      }
-
-      let element: ASTElement = createASTElement(tag, attrs, currentParent)
-      if (ns) {
-        element.ns = ns
-      }
-
-      if (__DEV__) {
-        if (options.outputSourceRange) {
-          element.start = start
-          element.end = end
-          element.rawAttrsMap = element.attrsList.reduce((cumulated, attr) => {
-            cumulated[attr.name] = attr
-            return cumulated
-          }, {})
-        }
-        attrs.forEach(attr => {
-          if (invalidAttributeRE.test(attr.name)) {
-            warn(
-              `Invalid dynamic argument expression: attribute names cannot contain ` +
-                `spaces, quotes, <, >, / or =.`,
-              options.outputSourceRange
-                ? {
-                    start: attr.start! + attr.name.indexOf(`[`),
-                    end: attr.start! + attr.name.length
-                  }
-                : undefined
-            )
-          }
-        })
-      }
-
-      if (isForbiddenTag(element) && !isServerRendering()) {
-        element.forbidden = true
-        __DEV__ &&
-          warn(
-            'Templates should only be responsible for mapping the state to the ' +
-              'UI. Avoid placing tags with side-effects in your templates, such as ' +
-              `<${tag}>` +
-              ', as they will not be parsed.',
-            { start: element.start }
-          )
-      }
-
-      // apply pre-transforms
-      for (let i = 0; i < preTransforms.length; i++) {
-        element = preTransforms[i](element, options) || element
-      }
-
-      if (!inVPre) {
-        processPre(element)
-        if (element.pre) {
-          inVPre = true
-        }
-      }
-      if (platformIsPreTag(element.tag)) {
-        inPre = true
-      }
-      if (inVPre) {
-        processRawAttrs(element)
-      } else if (!element.processed) {
-        // structural directives
-        processFor(element)
-        processIf(element)
-        processOnce(element)
-      }
-
-      if (!root) {
-        root = element
-        if (__DEV__) {
-          checkRootConstraints(root)
-        }
-      }
-
-      if (!unary) {
-        currentParent = element
-        stack.push(element)
-      } else {
-        closeElement(element)
-      }
-    },
-
-    end(tag, start, end) {
-      const element = stack[stack.length - 1]
-      // pop stack
-      stack.length -= 1
-      currentParent = stack[stack.length - 1]
-      if (__DEV__ && options.outputSourceRange) {
-        element.end = end
-      }
-      closeElement(element)
-    },
-
-    chars(text: string, start?: number, end?: number) {
-      if (!currentParent) {
-        if (__DEV__) {
-          if (text === template) {
-            warnOnce(
-              'Component template requires a root element, rather than just text.',
-              { start }
-            )
-          } else if ((text = text.trim())) {
-            warnOnce(`text "${text}" outside root element will be ignored.`, {
-              start
-            })
-          }
-        }
-        return
-      }
-      // IE textarea placeholder bug
-      /* istanbul ignore if */
-      if (
-        isIE &&
-        currentParent.tag === 'textarea' &&
-        currentParent.attrsMap.placeholder === text
-      ) {
-        return
-      }
-      const children = currentParent.children
-      if (inPre || text.trim()) {
-        text = isTextTag(currentParent)
-          ? text
-          : (decodeHTMLCached(text) as string)
-      } else if (!children.length) {
-        // remove the whitespace-only node right after an opening tag
-        text = ''
-      } else if (whitespaceOption) {
-        if (whitespaceOption === 'condense') {
-          // in condense mode, remove the whitespace node if it contains
-          // line break, otherwise condense to a single space
-          text = lineBreakRE.test(text) ? '' : ' '
-        } else {
-          text = ' '
-        }
-      } else {
-        text = preserveWhitespace ? ' ' : ''
-      }
-      if (text) {
-        if (!inPre && whitespaceOption === 'condense') {
-          // condense consecutive whitespaces into single space
-          text = text.replace(whitespaceRE, ' ')
-        }
-        let res
-        let child: ASTNode | undefined
-        if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) {
-          child = {
-            type: 2,
-            expression: res.expression,
-            tokens: res.tokens,
-            text
-          }
-        } else if (
-          text !== ' ' ||
-          !children.length ||
-          children[children.length - 1].text !== ' '
-        ) {
-          child = {
-            type: 3,
-            text
-          }
-        }
-        if (child) {
-          if (__DEV__ && options.outputSourceRange) {
-            child.start = start
-            child.end = end
-          }
-          children.push(child)
-        }
-      }
-    },
-    comment(text: string, start, end) {
-      // adding anything as a sibling to the root node is forbidden
-      // comments should still be allowed, but ignored
-      if (currentParent) {
-        const child: ASTText = {
-          type: 3,
-          text,
-          isComment: true
-        }
-        if (__DEV__ && options.outputSourceRange) {
-          child.start = start
-          child.end = end
-        }
-        currentParent.children.push(child)
-      }
-    }
-  })
-  return root
-}
-
-function processPre(el) {
-  if (getAndRemoveAttr(el, 'v-pre') != null) {
-    el.pre = true
-  }
-}
-
-function processRawAttrs(el) {
-  const list = el.attrsList
-  const len = list.length
-  if (len) {
-    const attrs: Array<ASTAttr> = (el.attrs = new Array(len))
-    for (let i = 0; i < len; i++) {
-      attrs[i] = {
-        name: list[i].name,
-        value: JSON.stringify(list[i].value)
-      }
-      if (list[i].start != null) {
-        attrs[i].start = list[i].start
-        attrs[i].end = list[i].end
-      }
-    }
-  } else if (!el.pre) {
-    // non root node in pre blocks with no attributes
-    el.plain = true
-  }
-}
-
-export function processElement(element: ASTElement, options: CompilerOptions) {
-  processKey(element)
-
-  // determine whether this is a plain element after
-  // removing structural attributes
-  element.plain =
-    !element.key && !element.scopedSlots && !element.attrsList.length
-
-  processRef(element)
-  processSlotContent(element)
-  processSlotOutlet(element)
-  processComponent(element)
-  for (let i = 0; i < transforms.length; i++) {
-    element = transforms[i](element, options) || element
-  }
-  processAttrs(element)
-  return element
-}
-
-function processKey(el) {
-  const exp = getBindingAttr(el, 'key')
-  if (exp) {
-    if (__DEV__) {
-      if (el.tag === 'template') {
-        warn(
-          `<template> cannot be keyed. Place the key on real elements instead.`,
-          getRawBindingAttr(el, 'key')
-        )
-      }
-      if (el.for) {
-        const iterator = el.iterator2 || el.iterator1
-        const parent = el.parent
-        if (
-          iterator &&
-          iterator === exp &&
-          parent &&
-          parent.tag === 'transition-group'
-        ) {
-          warn(
-            `Do not use v-for index as key on <transition-group> children, ` +
-              `this is the same as not using keys.`,
-            getRawBindingAttr(el, 'key'),
-            true /* tip */
-          )
-        }
-      }
-    }
-    el.key = exp
-  }
-}
-
-function processRef(el) {
-  const ref = getBindingAttr(el, 'ref')
-  if (ref) {
-    el.ref = ref
-    el.refInFor = checkInFor(el)
-  }
-}
-
-export function processFor(el: ASTElement) {
-  let exp
-  if ((exp = getAndRemoveAttr(el, 'v-for'))) {
-    const res = parseFor(exp)
-    if (res) {
-      extend(el, res)
-    } else if (__DEV__) {
-      warn(`Invalid v-for expression: ${exp}`, el.rawAttrsMap['v-for'])
-    }
-  }
-}
-
-type ForParseResult = {
-  for: string
-  alias: string
-  iterator1?: string
-  iterator2?: string
-}
-
-export function parseFor(exp: string): ForParseResult | undefined {
-  const inMatch = exp.match(forAliasRE)
-  if (!inMatch) return
-  const res: any = {}
-  res.for = inMatch[2].trim()
-  const alias = inMatch[1].trim().replace(stripParensRE, '')
-  const iteratorMatch = alias.match(forIteratorRE)
-  if (iteratorMatch) {
-    res.alias = alias.replace(forIteratorRE, '').trim()
-    res.iterator1 = iteratorMatch[1].trim()
-    if (iteratorMatch[2]) {
-      res.iterator2 = iteratorMatch[2].trim()
-    }
-  } else {
-    res.alias = alias
-  }
-  return res
-}
-
-function processIf(el) {
-  const exp = getAndRemoveAttr(el, 'v-if')
-  if (exp) {
-    el.if = exp
-    addIfCondition(el, {
-      exp: exp,
-      block: el
-    })
-  } else {
-    if (getAndRemoveAttr(el, 'v-else') != null) {
-      el.else = true
-    }
-    const elseif = getAndRemoveAttr(el, 'v-else-if')
-    if (elseif) {
-      el.elseif = elseif
-    }
-  }
-}
-
-function processIfConditions(el, parent) {
-  const prev = findPrevElement(parent.children)
-  if (prev && prev.if) {
-    addIfCondition(prev, {
-      exp: el.elseif,
-      block: el
-    })
-  } else if (__DEV__) {
-    warn(
-      `v-${el.elseif ? 'else-if="' + el.elseif + '"' : 'else'} ` +
-        `used on element <${el.tag}> without corresponding v-if.`,
-      el.rawAttrsMap[el.elseif ? 'v-else-if' : 'v-else']
-    )
-  }
-}
-
-function findPrevElement(children: Array<any>): ASTElement | void {
-  let i = children.length
-  while (i--) {
-    if (children[i].type === 1) {
-      return children[i]
-    } else {
-      if (__DEV__ && children[i].text !== ' ') {
-        warn(
-          `text "${children[i].text.trim()}" between v-if and v-else(-if) ` +
-            `will be ignored.`,
-          children[i]
-        )
-      }
-      children.pop()
-    }
-  }
-}
-
-export function addIfCondition(el: ASTElement, condition: ASTIfCondition) {
-  if (!el.ifConditions) {
-    el.ifConditions = []
-  }
-  el.ifConditions.push(condition)
-}
-
-function processOnce(el) {
-  const once = getAndRemoveAttr(el, 'v-once')
-  if (once != null) {
-    el.once = true
-  }
-}
-
-// handle content being passed to a component as slot,
-// e.g. <template slot="xxx">, <div slot-scope="xxx">
-function processSlotContent(el) {
-  let slotScope
-  if (el.tag === 'template') {
-    slotScope = getAndRemoveAttr(el, 'scope')
-    /* istanbul ignore if */
-    if (__DEV__ && slotScope) {
-      warn(
-        `the "scope" attribute for scoped slots have been deprecated and ` +
-          `replaced by "slot-scope" since 2.5. The new "slot-scope" attribute ` +
-          `can also be used on plain elements in addition to <template> to ` +
-          `denote scoped slots.`,
-        el.rawAttrsMap['scope'],
-        true
-      )
-    }
-    el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope')
-  } else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
-    /* istanbul ignore if */
-    if (__DEV__ && el.attrsMap['v-for']) {
-      warn(
-        `Ambiguous combined usage of slot-scope and v-for on <${el.tag}> ` +
-          `(v-for takes higher priority). Use a wrapper <template> for the ` +
-          `scoped slot to make it clearer.`,
-        el.rawAttrsMap['slot-scope'],
-        true
-      )
-    }
-    el.slotScope = slotScope
-  }
-
-  // slot="xxx"
-  const slotTarget = getBindingAttr(el, 'slot')
-  if (slotTarget) {
-    el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
-    el.slotTargetDynamic = !!(
-      el.attrsMap[':slot'] || el.attrsMap['v-bind:slot']
-    )
-    // preserve slot as an attribute for native shadow DOM compat
-    // only for non-scoped slots.
-    if (el.tag !== 'template' && !el.slotScope) {
-      addAttr(el, 'slot', slotTarget, getRawBindingAttr(el, 'slot'))
-    }
-  }
-
-  // 2.6 v-slot syntax
-  if (process.env.NEW_SLOT_SYNTAX) {
-    if (el.tag === 'template') {
-      // v-slot on <template>
-      const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
-      if (slotBinding) {
-        if (__DEV__) {
-          if (el.slotTarget || el.slotScope) {
-            warn(`Unexpected mixed usage of different slot syntaxes.`, el)
-          }
-          if (el.parent && !maybeComponent(el.parent)) {
-            warn(
-              `<template v-slot> can only appear at the root level inside ` +
-                `the receiving component`,
-              el
-            )
-          }
-        }
-        const { name, dynamic } = getSlotName(slotBinding)
-        el.slotTarget = name
-        el.slotTargetDynamic = dynamic
-        el.slotScope = slotBinding.value || emptySlotScopeToken // force it into a scoped slot for perf
-      }
-    } else {
-      // v-slot on component, denotes default slot
-      const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
-      if (slotBinding) {
-        if (__DEV__) {
-          if (!maybeComponent(el)) {
-            warn(
-              `v-slot can only be used on components or <template>.`,
-              slotBinding
-            )
-          }
-          if (el.slotScope || el.slotTarget) {
-            warn(`Unexpected mixed usage of different slot syntaxes.`, el)
-          }
-          if (el.scopedSlots) {
-            warn(
-              `To avoid scope ambiguity, the default slot should also use ` +
-                `<template> syntax when there are other named slots.`,
-              slotBinding
-            )
-          }
-        }
-        // add the component's children to its default slot
-        const slots = el.scopedSlots || (el.scopedSlots = {})
-        const { name, dynamic } = getSlotName(slotBinding)
-        const slotContainer = (slots[name] = createASTElement(
-          'template',
-          [],
-          el
-        ))
-        slotContainer.slotTarget = name
-        slotContainer.slotTargetDynamic = dynamic
-        slotContainer.children = el.children.filter((c: any) => {
-          if (!c.slotScope) {
-            c.parent = slotContainer
-            return true
-          }
-        })
-        slotContainer.slotScope = slotBinding.value || emptySlotScopeToken
-        // remove children as they are returned from scopedSlots now
-        el.children = []
-        // mark el non-plain so data gets generated
-        el.plain = false
-      }
-    }
-  }
-}
-
-function getSlotName(binding) {
-  let name = binding.name.replace(slotRE, '')
-  if (!name) {
-    if (binding.name[0] !== '#') {
-      name = 'default'
-    } else if (__DEV__) {
-      warn(`v-slot shorthand syntax requires a slot name.`, binding)
-    }
-  }
-  return dynamicArgRE.test(name)
-    ? // dynamic [name]
-      { name: name.slice(1, -1), dynamic: true }
-    : // static name
-      { name: `"${name}"`, dynamic: false }
-}
-
-// handle <slot/> outlets
-function processSlotOutlet(el) {
-  if (el.tag === 'slot') {
-    el.slotName = getBindingAttr(el, 'name')
-    if (__DEV__ && el.key) {
-      warn(
-        `\`key\` does not work on <slot> because slots are abstract outlets ` +
-          `and can possibly expand into multiple elements. ` +
-          `Use the key on a wrapping element instead.`,
-        getRawBindingAttr(el, 'key')
-      )
-    }
-  }
-}
-
-function processComponent(el) {
-  let binding
-  if ((binding = getBindingAttr(el, 'is'))) {
-    el.component = binding
-  }
-  if (getAndRemoveAttr(el, 'inline-template') != null) {
-    el.inlineTemplate = true
-  }
-}
-
-function processAttrs(el) {
-  const list = el.attrsList
-  let i, l, name, rawName, value, modifiers, syncGen, isDynamic
-  for (i = 0, l = list.length; i < l; i++) {
-    name = rawName = list[i].name
-    value = list[i].value
-    if (dirRE.test(name)) {
-      // mark element as dynamic
-      el.hasBindings = true
-      // modifiers
-      modifiers = parseModifiers(name.replace(dirRE, ''))
-      // support .foo shorthand syntax for the .prop modifier
-      if (process.env.VBIND_PROP_SHORTHAND && propBindRE.test(name)) {
-        ;(modifiers || (modifiers = {})).prop = true
-        name = `.` + name.slice(1).replace(modifierRE, '')
-      } else if (modifiers) {
-        name = name.replace(modifierRE, '')
-      }
-      if (bindRE.test(name)) {
-        // v-bind
-        name = name.replace(bindRE, '')
-        value = parseFilters(value)
-        isDynamic = dynamicArgRE.test(name)
-        if (isDynamic) {
-          name = name.slice(1, -1)
-        }
-        if (__DEV__ && value.trim().length === 0) {
-          warn(
-            `The value for a v-bind expression cannot be empty. Found in "v-bind:${name}"`
-          )
-        }
-        if (modifiers) {
-          if (modifiers.prop && !isDynamic) {
-            name = camelize(name)
-            if (name === 'innerHtml') name = 'innerHTML'
-          }
-          if (modifiers.camel && !isDynamic) {
-            name = camelize(name)
-          }
-          if (modifiers.sync) {
-            syncGen = genAssignmentCode(value, `$event`)
-            if (!isDynamic) {
-              addHandler(
-                el,
-                `update:${camelize(name)}`,
-                syncGen,
-                null,
-                false,
-                warn,
-                list[i]
-              )
-              if (hyphenate(name) !== camelize(name)) {
-                addHandler(
-                  el,
-                  `update:${hyphenate(name)}`,
-                  syncGen,
-                  null,
-                  false,
-                  warn,
-                  list[i]
-                )
-              }
-            } else {
-              // handler w/ dynamic event name
-              addHandler(
-                el,
-                `"update:"+(${name})`,
-                syncGen,
-                null,
-                false,
-                warn,
-                list[i],
-                true // dynamic
-              )
-            }
-          }
-        }
-        if (
-          (modifiers && modifiers.prop) ||
-          (!el.component && platformMustUseProp(el.tag, el.attrsMap.type, name))
-        ) {
-          addProp(el, name, value, list[i], isDynamic)
-        } else {
-          addAttr(el, name, value, list[i], isDynamic)
-        }
-      } else if (onRE.test(name)) {
-        // v-on
-        name = name.replace(onRE, '')
-        isDynamic = dynamicArgRE.test(name)
-        if (isDynamic) {
-          name = name.slice(1, -1)
-        }
-        addHandler(el, name, value, modifiers, false, warn, list[i], isDynamic)
-      } else {
-        // normal directives
-        name = name.replace(dirRE, '')
-        // parse arg
-        const argMatch = name.match(argRE)
-        let arg = argMatch && argMatch[1]
-        isDynamic = false
-        if (arg) {
-          name = name.slice(0, -(arg.length + 1))
-          if (dynamicArgRE.test(arg)) {
-            arg = arg.slice(1, -1)
-            isDynamic = true
-          }
-        }
-        addDirective(
-          el,
-          name,
-          rawName,
-          value,
-          arg,
-          isDynamic,
-          modifiers,
-          list[i]
-        )
-        if (__DEV__ && name === 'model') {
-          checkForAliasModel(el, value)
-        }
-      }
-    } else {
-      // literal attribute
-      if (__DEV__) {
-        const res = parseText(value, delimiters)
-        if (res) {
-          warn(
-            `${name}="${value}": ` +
-              'Interpolation inside attributes has been removed. ' +
-              'Use v-bind or the colon shorthand instead. For example, ' +
-              'instead of <div id="{{ val }}">, use <div :id="val">.',
-            list[i]
-          )
-        }
-      }
-      addAttr(el, name, JSON.stringify(value), list[i])
-      // #6887 firefox doesn't update muted state if set via attribute
-      // even immediately after element creation
-      if (
-        !el.component &&
-        name === 'muted' &&
-        platformMustUseProp(el.tag, el.attrsMap.type, name)
-      ) {
-        addProp(el, name, 'true', list[i])
-      }
-    }
-  }
-}
-
-function checkInFor(el: ASTElement): boolean {
-  let parent: ASTElement | void = el
-  while (parent) {
-    if (parent.for !== undefined) {
-      return true
-    }
-    parent = parent.parent
-  }
-  return false
-}
-
-function parseModifiers(name: string): Object | void {
-  const match = name.match(modifierRE)
-  if (match) {
-    const ret = {}
-    match.forEach(m => {
-      ret[m.slice(1)] = true
-    })
-    return ret
-  }
-}
-
-function makeAttrsMap(attrs: Array<Record<string, any>>): Record<string, any> {
-  const map = {}
-  for (let i = 0, l = attrs.length; i < l; i++) {
-    if (__DEV__ && map[attrs[i].name] && !isIE && !isEdge) {
-      warn('duplicate attribute: ' + attrs[i].name, attrs[i])
-    }
-    map[attrs[i].name] = attrs[i].value
-  }
-  return map
-}
-
-// for script (e.g. type="x/template") or style, do not decode content
-function isTextTag(el): boolean {
-  return el.tag === 'script' || el.tag === 'style'
-}
-
-function isForbiddenTag(el): boolean {
-  return (
-    el.tag === 'style' ||
-    (el.tag === 'script' &&
-      (!el.attrsMap.type || el.attrsMap.type === 'text/javascript'))
-  )
-}
-
-const ieNSBug = /^xmlns:NS\d+/
-const ieNSPrefix = /^NS\d+:/
-
-/* istanbul ignore next */
-function guardIESVGBug(attrs) {
-  const res: any[] = []
-  for (let i = 0; i < attrs.length; i++) {
-    const attr = attrs[i]
-    if (!ieNSBug.test(attr.name)) {
-      attr.name = attr.name.replace(ieNSPrefix, '')
-      res.push(attr)
-    }
-  }
-  return res
-}
-
-function checkForAliasModel(el, value) {
-  let _el = el
-  while (_el) {
-    if (_el.for && _el.alias === value) {
-      warn(
-        `<${el.tag} v-model="${value}">: ` +
-          `You are binding v-model directly to a v-for iteration alias. ` +
-          `This will not be able to modify the v-for source array because ` +
-          `writing to the alias is like modifying a function local variable. ` +
-          `Consider using an array of objects and use v-model on an object property instead.`,
-        el.rawAttrsMap['v-model']
-      )
-    }
-    _el = _el.parent
-  }
-}
diff --git a/src/compiler/parser/text-parser.ts b/src/compiler/parser/text-parser.ts
deleted file mode 100644
index ea7387ffff8..00000000000
--- a/src/compiler/parser/text-parser.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { cached } from 'shared/util'
-import { parseFilters } from './filter-parser'
-
-const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g
-const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g
-
-const buildRegex = cached(delimiters => {
-  const open = delimiters[0].replace(regexEscapeRE, '\\$&')
-  const close = delimiters[1].replace(regexEscapeRE, '\\$&')
-  return new RegExp(open + '((?:.|\\n)+?)' + close, 'g')
-})
-
-type TextParseResult = {
-  expression: string
-  tokens: Array<string | { '@binding': string }>
-}
-
-export function parseText(
-  text: string,
-  delimiters?: [string, string]
-): TextParseResult | void {
-  //@ts-expect-error
-  const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE
-  if (!tagRE.test(text)) {
-    return
-  }
-  const tokens: string[] = []
-  const rawTokens: any[] = []
-  let lastIndex = (tagRE.lastIndex = 0)
-  let match, index, tokenValue
-  while ((match = tagRE.exec(text))) {
-    index = match.index
-    // push text token
-    if (index > lastIndex) {
-      rawTokens.push((tokenValue = text.slice(lastIndex, index)))
-      tokens.push(JSON.stringify(tokenValue))
-    }
-    // tag token
-    const exp = parseFilters(match[1].trim())
-    tokens.push(`_s(${exp})`)
-    rawTokens.push({ '@binding': exp })
-    lastIndex = index + match[0].length
-  }
-  if (lastIndex < text.length) {
-    rawTokens.push((tokenValue = text.slice(lastIndex)))
-    tokens.push(JSON.stringify(tokenValue))
-  }
-  return {
-    expression: tokens.join('+'),
-    tokens: rawTokens
-  }
-}
diff --git a/src/compiler/resolve-slots.js b/src/compiler/resolve-slots.js
new file mode 100644
index 00000000000..efa0de30a81
--- /dev/null
+++ b/src/compiler/resolve-slots.js
@@ -0,0 +1,76 @@
+import { parseTemplate } from '../parsers/template'
+import {
+  isTemplate,
+  toArray,
+  getBindAttr,
+  warn
+} from '../util/index'
+
+/**
+ * Scan and determine slot content distribution.
+ * We do this during transclusion instead at compile time so that
+ * the distribution is decoupled from the compilation order of
+ * the slots.
+ *
+ * @param {Element|DocumentFragment} template
+ * @param {Element} content
+ * @param {Vue} vm
+ */
+
+export function resolveSlots (vm, content) {
+  if (!content) {
+    return
+  }
+  var contents = vm._slotContents = Object.create(null)
+  var el, name
+  for (var i = 0, l = content.children.length; i < l; i++) {
+    el = content.children[i]
+    /* eslint-disable no-cond-assign */
+    if (name = el.getAttribute('slot')) {
+      (contents[name] || (contents[name] = [])).push(el)
+    }
+    /* eslint-enable no-cond-assign */
+    if (process.env.NODE_ENV !== 'production' && getBindAttr(el, 'slot')) {
+      warn('The "slot" attribute must be static.', vm.$parent)
+    }
+  }
+  for (name in contents) {
+    contents[name] = extractFragment(contents[name], content)
+  }
+  if (content.hasChildNodes()) {
+    const nodes = content.childNodes
+    if (
+      nodes.length === 1 &&
+      nodes[0].nodeType === 3 &&
+      !nodes[0].data.trim()
+    ) {
+      return
+    }
+    contents['default'] = extractFragment(content.childNodes, content)
+  }
+}
+
+/**
+ * Extract qualified content nodes from a node list.
+ *
+ * @param {NodeList} nodes
+ * @return {DocumentFragment}
+ */
+
+function extractFragment (nodes, parent) {
+  var frag = document.createDocumentFragment()
+  nodes = toArray(nodes)
+  for (var i = 0, l = nodes.length; i < l; i++) {
+    var node = nodes[i]
+    if (
+      isTemplate(node) &&
+      !node.hasAttribute('v-if') &&
+      !node.hasAttribute('v-for')
+    ) {
+      parent.removeChild(node)
+      node = parseTemplate(node, true)
+    }
+    frag.appendChild(node)
+  }
+  return frag
+}
diff --git a/src/compiler/to-function.ts b/src/compiler/to-function.ts
deleted file mode 100644
index de0b95c1d35..00000000000
--- a/src/compiler/to-function.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-import { noop, extend } from 'shared/util'
-import { warn as baseWarn, tip } from 'core/util/debug'
-import { generateCodeFrame } from './codeframe'
-import type { Component } from 'types/component'
-import { CompilerOptions } from 'types/compiler'
-
-type CompiledFunctionResult = {
-  render: Function
-  staticRenderFns: Array<Function>
-}
-
-function createFunction(code, errors) {
-  try {
-    return new Function(code)
-  } catch (err: any) {
-    errors.push({ err, code })
-    return noop
-  }
-}
-
-export function createCompileToFunctionFn(compile: Function): Function {
-  const cache = Object.create(null)
-
-  return function compileToFunctions(
-    template: string,
-    options?: CompilerOptions,
-    vm?: Component
-  ): CompiledFunctionResult {
-    options = extend({}, options)
-    const warn = options.warn || baseWarn
-    delete options.warn
-
-    /* istanbul ignore if */
-    if (__DEV__) {
-      // detect possible CSP restriction
-      try {
-        new Function('return 1')
-      } catch (e: any) {
-        if (e.toString().match(/unsafe-eval|CSP/)) {
-          warn(
-            'It seems you are using the standalone build of Vue.js in an ' +
-              'environment with Content Security Policy that prohibits unsafe-eval. ' +
-              'The template compiler cannot work in this environment. Consider ' +
-              'relaxing the policy to allow unsafe-eval or pre-compiling your ' +
-              'templates into render functions.'
-          )
-        }
-      }
-    }
-
-    // check cache
-    const key = options.delimiters
-      ? String(options.delimiters) + template
-      : template
-    if (cache[key]) {
-      return cache[key]
-    }
-
-    // compile
-    const compiled = compile(template, options)
-
-    // check compilation errors/tips
-    if (__DEV__) {
-      if (compiled.errors && compiled.errors.length) {
-        if (options.outputSourceRange) {
-          compiled.errors.forEach(e => {
-            warn(
-              `Error compiling template:\n\n${e.msg}\n\n` +
-                generateCodeFrame(template, e.start, e.end),
-              vm
-            )
-          })
-        } else {
-          warn(
-            `Error compiling template:\n\n${template}\n\n` +
-              compiled.errors.map(e => `- ${e}`).join('\n') +
-              '\n',
-            vm
-          )
-        }
-      }
-      if (compiled.tips && compiled.tips.length) {
-        if (options.outputSourceRange) {
-          compiled.tips.forEach(e => tip(e.msg, vm))
-        } else {
-          compiled.tips.forEach(msg => tip(msg, vm))
-        }
-      }
-    }
-
-    // turn code into functions
-    const res: any = {}
-    const fnGenErrors: any[] = []
-    res.render = createFunction(compiled.render, fnGenErrors)
-    res.staticRenderFns = compiled.staticRenderFns.map(code => {
-      return createFunction(code, fnGenErrors)
-    })
-
-    // check function generation errors.
-    // this should only happen if there is a bug in the compiler itself.
-    // mostly for codegen development use
-    /* istanbul ignore if */
-    if (__DEV__) {
-      if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) {
-        warn(
-          `Failed to generate render function:\n\n` +
-            fnGenErrors
-              .map(
-                ({ err, code }) => `${(err as any).toString()} in\n\n${code}\n`
-              )
-              .join('\n'),
-          vm
-        )
-      }
-    }
-
-    return (cache[key] = res)
-  }
-}
diff --git a/src/compiler/transclude.js b/src/compiler/transclude.js
new file mode 100644
index 00000000000..4cf72c70681
--- /dev/null
+++ b/src/compiler/transclude.js
@@ -0,0 +1,163 @@
+import { parseText } from '../parsers/text'
+import { parseTemplate } from '../parsers/template'
+import {
+  warn,
+  isTemplate,
+  isFragment,
+  prepend,
+  extractContent,
+  createAnchor,
+  resolveAsset,
+  toArray,
+  addClass,
+  hasBindAttr
+} from '../util/index'
+
+const specialCharRE = /[^\w\-:\.]/
+
+/**
+ * Process an element or a DocumentFragment based on a
+ * instance option object. This allows us to transclude
+ * a template node/fragment before the instance is created,
+ * so the processed fragment can then be cloned and reused
+ * in v-for.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Element|DocumentFragment}
+ */
+
+export function transclude (el, options) {
+  // extract container attributes to pass them down
+  // to compiler, because they need to be compiled in
+  // parent scope. we are mutating the options object here
+  // assuming the same object will be used for compile
+  // right after this.
+  if (options) {
+    options._containerAttrs = extractAttrs(el)
+  }
+  // for template tags, what we want is its content as
+  // a documentFragment (for fragment instances)
+  if (isTemplate(el)) {
+    el = parseTemplate(el)
+  }
+  if (options) {
+    if (options._asComponent && !options.template) {
+      options.template = '<slot></slot>'
+    }
+    if (options.template) {
+      options._content = extractContent(el)
+      el = transcludeTemplate(el, options)
+    }
+  }
+  if (isFragment(el)) {
+    // anchors for fragment instance
+    // passing in `persist: true` to avoid them being
+    // discarded by IE during template cloning
+    prepend(createAnchor('v-start', true), el)
+    el.appendChild(createAnchor('v-end', true))
+  }
+  return el
+}
+
+/**
+ * Process the template option.
+ * If the replace option is true this will swap the $el.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Element|DocumentFragment}
+ */
+
+function transcludeTemplate (el, options) {
+  var template = options.template
+  var frag = parseTemplate(template, true)
+  if (frag) {
+    var replacer = frag.firstChild
+    if (!replacer) {
+      return frag
+    }
+    var tag = replacer.tagName && replacer.tagName.toLowerCase()
+    if (options.replace) {
+      /* istanbul ignore if */
+      if (el === document.body) {
+        process.env.NODE_ENV !== 'production' && warn(
+          'You are mounting an instance with a template to ' +
+          '<body>. This will replace <body> entirely. You ' +
+          'should probably use `replace: false` here.'
+        )
+      }
+      // there are many cases where the instance must
+      // become a fragment instance: basically anything that
+      // can create more than 1 root nodes.
+      if (
+        // multi-children template
+        frag.childNodes.length > 1 ||
+        // non-element template
+        replacer.nodeType !== 1 ||
+        // single nested component
+        tag === 'component' ||
+        resolveAsset(options, 'components', tag) ||
+        hasBindAttr(replacer, 'is') ||
+        // element directive
+        resolveAsset(options, 'elementDirectives', tag) ||
+        // for block
+        replacer.hasAttribute('v-for') ||
+        // if block
+        replacer.hasAttribute('v-if')
+      ) {
+        return frag
+      } else {
+        options._replacerAttrs = extractAttrs(replacer)
+        mergeAttrs(el, replacer)
+        return replacer
+      }
+    } else {
+      el.appendChild(frag)
+      return el
+    }
+  } else {
+    process.env.NODE_ENV !== 'production' && warn(
+      'Invalid template option: ' + template
+    )
+  }
+}
+
+/**
+ * Helper to extract a component container's attributes
+ * into a plain object array.
+ *
+ * @param {Element} el
+ * @return {Array}
+ */
+
+function extractAttrs (el) {
+  if (el.nodeType === 1 && el.hasAttributes()) {
+    return toArray(el.attributes)
+  }
+}
+
+/**
+ * Merge the attributes of two elements, and make sure
+ * the class names are merged properly.
+ *
+ * @param {Element} from
+ * @param {Element} to
+ */
+
+function mergeAttrs (from, to) {
+  var attrs = from.attributes
+  var i = attrs.length
+  var name, value
+  while (i--) {
+    name = attrs[i].name
+    value = attrs[i].value
+    if (!to.hasAttribute(name) && !specialCharRE.test(name)) {
+      to.setAttribute(name, value)
+    } else if (name === 'class' && !parseText(value) && (value = value.trim())) {
+      value.split(/\s+/).forEach(function (cls) {
+        addClass(to, cls)
+      })
+    }
+  }
+}
diff --git a/src/config.js b/src/config.js
new file mode 100644
index 00000000000..bfe53390f46
--- /dev/null
+++ b/src/config.js
@@ -0,0 +1,111 @@
+import { compileRegex } from './parsers/text'
+
+let delimiters = ['{{', '}}']
+let unsafeDelimiters = ['{{{', '}}}']
+
+const config = {
+
+  /**
+   * Whether to print debug messages.
+   * Also enables stack trace for warnings.
+   *
+   * @type {Boolean}
+   */
+
+  debug: false,
+
+  /**
+   * Whether to suppress warnings.
+   *
+   * @type {Boolean}
+   */
+
+  silent: false,
+
+  /**
+   * Whether to use async rendering.
+   */
+
+  async: true,
+
+  /**
+   * Whether to warn against errors caught when evaluating
+   * expressions.
+   */
+
+  warnExpressionErrors: true,
+
+  /**
+   * Whether to allow devtools inspection.
+   * Disabled by default in production builds.
+   */
+
+  devtools: process.env.NODE_ENV !== 'production',
+
+  /**
+   * Internal flag to indicate the delimiters have been
+   * changed.
+   *
+   * @type {Boolean}
+   */
+
+  _delimitersChanged: true,
+
+  /**
+   * List of asset types that a component can own.
+   *
+   * @type {Array}
+   */
+
+  _assetTypes: [
+    'component',
+    'directive',
+    'elementDirective',
+    'filter',
+    'transition',
+    'partial'
+  ],
+
+  /**
+   * prop binding modes
+   */
+
+  _propBindingModes: {
+    ONE_WAY: 0,
+    TWO_WAY: 1,
+    ONE_TIME: 2
+  },
+
+  /**
+   * Max circular updates allowed in a batcher flush cycle.
+   */
+
+  _maxUpdateCount: 100,
+
+  /**
+   * Interpolation delimiters. Changing these would trigger
+   * the text parser to re-compile the regular expressions.
+   *
+   * @type {Array<String>}
+   */
+
+  get delimiters () {
+    return delimiters
+  },
+
+  set delimiters (val) {
+    delimiters = val
+    compileRegex()
+  },
+
+  get unsafeDelimiters () {
+    return unsafeDelimiters
+  },
+
+  set unsafeDelimiters (val) {
+    unsafeDelimiters = val
+    compileRegex()
+  }
+}
+
+export default config
diff --git a/src/core/components/index.ts b/src/core/components/index.ts
deleted file mode 100644
index 5461c763841..00000000000
--- a/src/core/components/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import KeepAlive from './keep-alive'
-
-export default {
-  KeepAlive
-}
diff --git a/src/core/components/keep-alive.ts b/src/core/components/keep-alive.ts
deleted file mode 100644
index b82152987e5..00000000000
--- a/src/core/components/keep-alive.ts
+++ /dev/null
@@ -1,171 +0,0 @@
-import { isRegExp, isArray, remove } from 'shared/util'
-import { getFirstComponentChild } from 'core/vdom/helpers/index'
-import type VNode from 'core/vdom/vnode'
-import type { VNodeComponentOptions } from 'types/vnode'
-import type { Component } from 'types/component'
-import { getComponentName } from '../vdom/create-component'
-
-type CacheEntry = {
-  name?: string
-  tag?: string
-  componentInstance?: Component
-}
-
-type CacheEntryMap = Record<string, CacheEntry | null>
-
-function _getComponentName(opts?: VNodeComponentOptions): string | null {
-  return opts && (getComponentName(opts.Ctor.options as any) || opts.tag)
-}
-
-function matches(
-  pattern: string | RegExp | Array<string>,
-  name: string
-): boolean {
-  if (isArray(pattern)) {
-    return pattern.indexOf(name) > -1
-  } else if (typeof pattern === 'string') {
-    return pattern.split(',').indexOf(name) > -1
-  } else if (isRegExp(pattern)) {
-    return pattern.test(name)
-  }
-  /* istanbul ignore next */
-  return false
-}
-
-function pruneCache(
-  keepAliveInstance: {
-    cache: CacheEntryMap
-    keys: string[]
-    _vnode: VNode
-    $vnode: VNode
-  },
-  filter: Function
-) {
-  const { cache, keys, _vnode, $vnode } = keepAliveInstance
-  for (const key in cache) {
-    const entry = cache[key]
-    if (entry) {
-      const name = entry.name
-      if (name && !filter(name)) {
-        pruneCacheEntry(cache, key, keys, _vnode)
-      }
-    }
-  }
-  $vnode.componentOptions!.children = undefined
-}
-
-function pruneCacheEntry(
-  cache: CacheEntryMap,
-  key: string,
-  keys: Array<string>,
-  current?: VNode
-) {
-  const entry = cache[key]
-  if (entry && (!current || entry.tag !== current.tag)) {
-    // @ts-expect-error can be undefined
-    entry.componentInstance.$destroy()
-  }
-  cache[key] = null
-  remove(keys, key)
-}
-
-const patternTypes: Array<Function> = [String, RegExp, Array]
-
-// TODO defineComponent
-export default {
-  name: 'keep-alive',
-  abstract: true,
-
-  props: {
-    include: patternTypes,
-    exclude: patternTypes,
-    max: [String, Number]
-  },
-
-  methods: {
-    cacheVNode() {
-      const { cache, keys, vnodeToCache, keyToCache } = this
-      if (vnodeToCache) {
-        const { tag, componentInstance, componentOptions } = vnodeToCache
-        cache[keyToCache] = {
-          name: _getComponentName(componentOptions),
-          tag,
-          componentInstance
-        }
-        keys.push(keyToCache)
-        // prune oldest entry
-        if (this.max && keys.length > parseInt(this.max)) {
-          pruneCacheEntry(cache, keys[0], keys, this._vnode)
-        }
-        this.vnodeToCache = null
-      }
-    }
-  },
-
-  created() {
-    this.cache = Object.create(null)
-    this.keys = []
-  },
-
-  destroyed() {
-    for (const key in this.cache) {
-      pruneCacheEntry(this.cache, key, this.keys)
-    }
-  },
-
-  mounted() {
-    this.cacheVNode()
-    this.$watch('include', val => {
-      pruneCache(this, name => matches(val, name))
-    })
-    this.$watch('exclude', val => {
-      pruneCache(this, name => !matches(val, name))
-    })
-  },
-
-  updated() {
-    this.cacheVNode()
-  },
-
-  render() {
-    const slot = this.$slots.default
-    const vnode = getFirstComponentChild(slot)
-    const componentOptions = vnode && vnode.componentOptions
-    if (componentOptions) {
-      // check pattern
-      const name = _getComponentName(componentOptions)
-      const { include, exclude } = this
-      if (
-        // not included
-        (include && (!name || !matches(include, name))) ||
-        // excluded
-        (exclude && name && matches(exclude, name))
-      ) {
-        return vnode
-      }
-
-      const { cache, keys } = this
-      const key =
-        vnode.key == null
-          ? // same constructor may get registered as different local components
-            // so cid alone is not enough (#3269)
-            componentOptions.Ctor.cid +
-            (componentOptions.tag ? `::${componentOptions.tag}` : '')
-          : vnode.key
-      if (cache[key]) {
-        vnode.componentInstance = cache[key].componentInstance
-        // make current key freshest
-        remove(keys, key)
-        keys.push(key)
-      } else {
-        // delay setting the cache until update
-        this.vnodeToCache = vnode
-        this.keyToCache = key
-      }
-
-      // @ts-expect-error can vnode.data can be undefined
-      vnode.data.keepAlive = true
-    }
-    return vnode || (slot && slot[0])
-  }
-}
diff --git a/src/core/config.ts b/src/core/config.ts
deleted file mode 100644
index 97b17adcf45..00000000000
--- a/src/core/config.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import { no, noop, identity } from 'shared/util'
-
-import { LIFECYCLE_HOOKS } from 'shared/constants'
-import type { Component } from 'types/component'
-
-/**
- * @internal
- */
-export interface Config {
-  // user
-  optionMergeStrategies: { [key: string]: Function }
-  silent: boolean
-  productionTip: boolean
-  performance: boolean
-  devtools: boolean
-  errorHandler?: (err: Error, vm: Component | null, info: string) => void
-  warnHandler?: (msg: string, vm: Component | null, trace: string) => void
-  ignoredElements: Array<string | RegExp>
-  keyCodes: { [key: string]: number | Array<number> }
-
-  // platform
-  isReservedTag: (x: string) => boolean | undefined
-  isReservedAttr: (x: string) => true | undefined
-  parsePlatformTagName: (x: string) => string
-  isUnknownElement: (x: string) => boolean
-  getTagNamespace: (x: string) => string | undefined
-  mustUseProp: (tag: string, type?: string | null, name?: string) => boolean
-
-  // private
-  async: boolean
-
-  // legacy
-  _lifecycleHooks: Array<string>
-}
-
-export default {
-  /**
-   * Option merge strategies (used in core/util/options)
-   */
-  // $flow-disable-line
-  optionMergeStrategies: Object.create(null),
-
-  /**
-   * Whether to suppress warnings.
-   */
-  silent: false,
-
-  /**
-   * Show production mode tip message on boot?
-   */
-  productionTip: __DEV__,
-
-  /**
-   * Whether to enable devtools
-   */
-  devtools: __DEV__,
-
-  /**
-   * Whether to record perf
-   */
-  performance: false,
-
-  /**
-   * Error handler for watcher errors
-   */
-  errorHandler: null,
-
-  /**
-   * Warn handler for watcher warns
-   */
-  warnHandler: null,
-
-  /**
-   * Ignore certain custom elements
-   */
-  ignoredElements: [],
-
-  /**
-   * Custom user key aliases for v-on
-   */
-  // $flow-disable-line
-  keyCodes: Object.create(null),
-
-  /**
-   * Check if a tag is reserved so that it cannot be registered as a
-   * component. This is platform-dependent and may be overwritten.
-   */
-  isReservedTag: no,
-
-  /**
-   * Check if an attribute is reserved so that it cannot be used as a component
-   * prop. This is platform-dependent and may be overwritten.
-   */
-  isReservedAttr: no,
-
-  /**
-   * Check if a tag is an unknown element.
-   * Platform-dependent.
-   */
-  isUnknownElement: no,
-
-  /**
-   * Get the namespace of an element
-   */
-  getTagNamespace: noop,
-
-  /**
-   * Parse the real tag name for the specific platform.
-   */
-  parsePlatformTagName: identity,
-
-  /**
-   * Check if an attribute must be bound using property, e.g. value
-   * Platform-dependent.
-   */
-  mustUseProp: no,
-
-  /**
-   * Perform updates asynchronously. Intended to be used by Vue Test Utils
-   * This will significantly reduce performance if set to false.
-   */
-  async: true,
-
-  /**
-   * Exposed for legacy reasons
-   */
-  _lifecycleHooks: LIFECYCLE_HOOKS
-} as unknown as Config
diff --git a/src/core/global-api/assets.ts b/src/core/global-api/assets.ts
deleted file mode 100644
index c26f284d4a8..00000000000
--- a/src/core/global-api/assets.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { ASSET_TYPES } from 'shared/constants'
-import type { GlobalAPI } from 'types/global-api'
-import { isFunction, isPlainObject, validateComponentName } from '../util/index'
-
-export function initAssetRegisters(Vue: GlobalAPI) {
-  /**
-   * Create asset registration methods.
-   */
-  ASSET_TYPES.forEach(type => {
-    // @ts-expect-error function is not exact same type
-    Vue[type] = function (
-      id: string,
-      definition?: Function | Object
-    ): Function | Object | void {
-      if (!definition) {
-        return this.options[type + 's'][id]
-      } else {
-        /* istanbul ignore if */
-        if (__DEV__ && type === 'component') {
-          validateComponentName(id)
-        }
-        if (type === 'component' && isPlainObject(definition)) {
-          // @ts-expect-error
-          definition.name = definition.name || id
-          definition = this.options._base.extend(definition)
-        }
-        if (type === 'directive' && isFunction(definition)) {
-          definition = { bind: definition, update: definition }
-        }
-        this.options[type + 's'][id] = definition
-        return definition
-      }
-    }
-  })
-}
diff --git a/src/core/global-api/extend.ts b/src/core/global-api/extend.ts
deleted file mode 100644
index 800e8c2329e..00000000000
--- a/src/core/global-api/extend.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import { ASSET_TYPES } from 'shared/constants'
-import type { Component } from 'types/component'
-import type { GlobalAPI } from 'types/global-api'
-import { defineComputed, proxy } from '../instance/state'
-import { extend, mergeOptions, validateComponentName } from '../util/index'
-import { getComponentName } from '../vdom/create-component'
-
-export function initExtend(Vue: GlobalAPI) {
-  /**
-   * Each instance constructor, including Vue, has a unique
-   * cid. This enables us to create wrapped "child
-   * constructors" for prototypal inheritance and cache them.
-   */
-  Vue.cid = 0
-  let cid = 1
-
-  /**
-   * Class inheritance
-   */
-  Vue.extend = function (extendOptions: any): typeof Component {
-    extendOptions = extendOptions || {}
-    const Super = this
-    const SuperId = Super.cid
-    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
-    if (cachedCtors[SuperId]) {
-      return cachedCtors[SuperId]
-    }
-
-    const name =
-      getComponentName(extendOptions) || getComponentName(Super.options)
-    if (__DEV__ && name) {
-      validateComponentName(name)
-    }
-
-    const Sub = function VueComponent(this: any, options: any) {
-      this._init(options)
-    } as unknown as typeof Component
-    Sub.prototype = Object.create(Super.prototype)
-    Sub.prototype.constructor = Sub
-    Sub.cid = cid++
-    Sub.options = mergeOptions(Super.options, extendOptions)
-    Sub['super'] = Super
-
-    // For props and computed properties, we define the proxy getters on
-    // the Vue instances at extension time, on the extended prototype. This
-    // avoids Object.defineProperty calls for each instance created.
-    if (Sub.options.props) {
-      initProps(Sub)
-    }
-    if (Sub.options.computed) {
-      initComputed(Sub)
-    }
-
-    // allow further extension/mixin/plugin usage
-    Sub.extend = Super.extend
-    Sub.mixin = Super.mixin
-    Sub.use = Super.use
-
-    // create asset registers, so extended classes
-    // can have their private assets too.
-    ASSET_TYPES.forEach(function (type) {
-      Sub[type] = Super[type]
-    })
-    // enable recursive self-lookup
-    if (name) {
-      Sub.options.components[name] = Sub
-    }
-
-    // keep a reference to the super options at extension time.
-    // later at instantiation we can check if Super's options have
-    // been updated.
-    Sub.superOptions = Super.options
-    Sub.extendOptions = extendOptions
-    Sub.sealedOptions = extend({}, Sub.options)
-
-    // cache constructor
-    cachedCtors[SuperId] = Sub
-    return Sub
-  }
-}
-
-function initProps(Comp: typeof Component) {
-  const props = Comp.options.props
-  for (const key in props) {
-    proxy(Comp.prototype, `_props`, key)
-  }
-}
-
-function initComputed(Comp: typeof Component) {
-  const computed = Comp.options.computed
-  for (const key in computed) {
-    defineComputed(Comp.prototype, key, computed[key])
-  }
-}
diff --git a/src/core/global-api/index.ts b/src/core/global-api/index.ts
deleted file mode 100644
index ca95ccf95c7..00000000000
--- a/src/core/global-api/index.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import config from '../config'
-import { initUse } from './use'
-import { initMixin } from './mixin'
-import { initExtend } from './extend'
-import { initAssetRegisters } from './assets'
-import { set, del } from '../observer/index'
-import { ASSET_TYPES } from 'shared/constants'
-import builtInComponents from '../components/index'
-import { observe } from 'core/observer/index'
-
-import {
-  warn,
-  extend,
-  nextTick,
-  mergeOptions,
-  defineReactive
-} from '../util/index'
-import type { GlobalAPI } from 'types/global-api'
-
-export function initGlobalAPI(Vue: GlobalAPI) {
-  // config
-  const configDef: Record<string, any> = {}
-  configDef.get = () => config
-  if (__DEV__) {
-    configDef.set = () => {
-      warn(
-        'Do not replace the Vue.config object, set individual fields instead.'
-      )
-    }
-  }
-  Object.defineProperty(Vue, 'config', configDef)
-
-  // exposed util methods.
-  // NOTE: these are not considered part of the public API - avoid relying on
-  // them unless you are aware of the risk.
-  Vue.util = {
-    warn,
-    extend,
-    mergeOptions,
-    defineReactive
-  }
-
-  Vue.set = set
-  Vue.delete = del
-  Vue.nextTick = nextTick
-
-  // 2.6 explicit observable API
-  Vue.observable = <T>(obj: T): T => {
-    observe(obj)
-    return obj
-  }
-
-  Vue.options = Object.create(null)
-  ASSET_TYPES.forEach(type => {
-    Vue.options[type + 's'] = Object.create(null)
-  })
-
-  // this is used to identify the "base" constructor to extend all plain-object
-  // components with in Weex's multi-instance scenarios.
-  Vue.options._base = Vue
-
-  extend(Vue.options.components, builtInComponents)
-
-  initUse(Vue)
-  initMixin(Vue)
-  initExtend(Vue)
-  initAssetRegisters(Vue)
-}
diff --git a/src/core/global-api/mixin.ts b/src/core/global-api/mixin.ts
deleted file mode 100644
index 7a4b00d97e9..00000000000
--- a/src/core/global-api/mixin.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import type { GlobalAPI } from 'types/global-api'
-import { mergeOptions } from '../util/index'
-
-export function initMixin(Vue: GlobalAPI) {
-  Vue.mixin = function (mixin: Object) {
-    this.options = mergeOptions(this.options, mixin)
-    return this
-  }
-}
diff --git a/src/core/global-api/use.ts b/src/core/global-api/use.ts
deleted file mode 100644
index 6ee4d518af4..00000000000
--- a/src/core/global-api/use.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import type { GlobalAPI } from 'types/global-api'
-import { toArray, isFunction } from '../util/index'
-
-export function initUse(Vue: GlobalAPI) {
-  Vue.use = function (plugin: Function | any) {
-    const installedPlugins =
-      this._installedPlugins || (this._installedPlugins = [])
-    if (installedPlugins.indexOf(plugin) > -1) {
-      return this
-    }
-
-    // additional parameters
-    const args = toArray(arguments, 1)
-    args.unshift(this)
-    if (isFunction(plugin.install)) {
-      plugin.install.apply(plugin, args)
-    } else if (isFunction(plugin)) {
-      plugin.apply(null, args)
-    }
-    installedPlugins.push(plugin)
-    return this
-  }
-}
diff --git a/src/core/index.ts b/src/core/index.ts
deleted file mode 100644
index 6dc63950ccb..00000000000
--- a/src/core/index.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import Vue from './instance/index'
-import { initGlobalAPI } from './global-api/index'
-import { isServerRendering } from 'core/util/env'
-import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
-import { version } from 'v3'
-
-initGlobalAPI(Vue)
-
-Object.defineProperty(Vue.prototype, '$isServer', {
-  get: isServerRendering
-})
-
-Object.defineProperty(Vue.prototype, '$ssrContext', {
-  get() {
-    /* istanbul ignore next */
-    return this.$vnode && this.$vnode.ssrContext
-  }
-})
-
-// expose FunctionalRenderContext for ssr runtime helper installation
-Object.defineProperty(Vue, 'FunctionalRenderContext', {
-  value: FunctionalRenderContext
-})
-
-Vue.version = version
-
-export default Vue
diff --git a/src/core/instance/events.ts b/src/core/instance/events.ts
deleted file mode 100644
index 3e6d6ab1df8..00000000000
--- a/src/core/instance/events.ts
+++ /dev/null
@@ -1,160 +0,0 @@
-import type { Component } from 'types/component'
-import {
-  tip,
-  toArray,
-  isArray,
-  hyphenate,
-  formatComponentName,
-  invokeWithErrorHandling
-} from '../util/index'
-import { updateListeners } from '../vdom/helpers/index'
-
-export function initEvents(vm: Component) {
-  vm._events = Object.create(null)
-  vm._hasHookEvent = false
-  // init parent attached events
-  const listeners = vm.$options._parentListeners
-  if (listeners) {
-    updateComponentListeners(vm, listeners)
-  }
-}
-
-let target: any
-
-function add(event, fn) {
-  target.$on(event, fn)
-}
-
-function remove(event, fn) {
-  target.$off(event, fn)
-}
-
-function createOnceHandler(event, fn) {
-  const _target = target
-  return function onceHandler() {
-    const res = fn.apply(null, arguments)
-    if (res !== null) {
-      _target.$off(event, onceHandler)
-    }
-  }
-}
-
-export function updateComponentListeners(
-  vm: Component,
-  listeners: Object,
-  oldListeners?: Object | null
-) {
-  target = vm
-  updateListeners(
-    listeners,
-    oldListeners || {},
-    add,
-    remove,
-    createOnceHandler,
-    vm
-  )
-  target = undefined
-}
-
-export function eventsMixin(Vue: typeof Component) {
-  const hookRE = /^hook:/
-  Vue.prototype.$on = function (
-    event: string | Array<string>,
-    fn: Function
-  ): Component {
-    const vm: Component = this
-    if (isArray(event)) {
-      for (let i = 0, l = event.length; i < l; i++) {
-        vm.$on(event[i], fn)
-      }
-    } else {
-      ;(vm._events[event] || (vm._events[event] = [])).push(fn)
-      // optimize hook:event cost by using a boolean flag marked at registration
-      // instead of a hash lookup
-      if (hookRE.test(event)) {
-        vm._hasHookEvent = true
-      }
-    }
-    return vm
-  }
-
-  Vue.prototype.$once = function (event: string, fn: Function): Component {
-    const vm: Component = this
-    function on() {
-      vm.$off(event, on)
-      fn.apply(vm, arguments)
-    }
-    on.fn = fn
-    vm.$on(event, on)
-    return vm
-  }
-
-  Vue.prototype.$off = function (
-    event?: string | Array<string>,
-    fn?: Function
-  ): Component {
-    const vm: Component = this
-    // all
-    if (!arguments.length) {
-      vm._events = Object.create(null)
-      return vm
-    }
-    // array of events
-    if (isArray(event)) {
-      for (let i = 0, l = event.length; i < l; i++) {
-        vm.$off(event[i], fn)
-      }
-      return vm
-    }
-    // specific event
-    const cbs = vm._events[event!]
-    if (!cbs) {
-      return vm
-    }
-    if (!fn) {
-      vm._events[event!] = null
-      return vm
-    }
-    // specific handler
-    let cb
-    let i = cbs.length
-    while (i--) {
-      cb = cbs[i]
-      if (cb === fn || cb.fn === fn) {
-        cbs.splice(i, 1)
-        break
-      }
-    }
-    return vm
-  }
-
-  Vue.prototype.$emit = function (event: string): Component {
-    const vm: Component = this
-    if (__DEV__) {
-      const lowerCaseEvent = event.toLowerCase()
-      if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
-        tip(
-          `Event "${lowerCaseEvent}" is emitted in component ` +
-            `${formatComponentName(
-              vm
-            )} but the handler is registered for "${event}". ` +
-            `Note that HTML attributes are case-insensitive and you cannot use ` +
-            `v-on to listen to camelCase events when using in-DOM templates. ` +
-            `You should probably use "${hyphenate(
-              event
-            )}" instead of "${event}".`
-        )
-      }
-    }
-    let cbs = vm._events[event]
-    if (cbs) {
-      cbs = cbs.length > 1 ? toArray(cbs) : cbs
-      const args = toArray(arguments, 1)
-      const info = `event handler for "${event}"`
-      for (let i = 0, l = cbs.length; i < l; i++) {
-        invokeWithErrorHandling(cbs[i], vm, args, vm, info)
-      }
-    }
-    return vm
-  }
-}
diff --git a/src/core/instance/index.ts b/src/core/instance/index.ts
deleted file mode 100644
index 4e7aab2d571..00000000000
--- a/src/core/instance/index.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { initMixin } from './init'
-import { stateMixin } from './state'
-import { renderMixin } from './render'
-import { eventsMixin } from './events'
-import { lifecycleMixin } from './lifecycle'
-import { warn } from '../util/index'
-import type { GlobalAPI } from 'types/global-api'
-
-function Vue(options) {
-  if (__DEV__ && !(this instanceof Vue)) {
-    warn('Vue is a constructor and should be called with the `new` keyword')
-  }
-  this._init(options)
-}
-
-//@ts-expect-error Vue has function type
-initMixin(Vue)
-//@ts-expect-error Vue has function type
-stateMixin(Vue)
-//@ts-expect-error Vue has function type
-eventsMixin(Vue)
-//@ts-expect-error Vue has function type
-lifecycleMixin(Vue)
-//@ts-expect-error Vue has function type
-renderMixin(Vue)
-
-export default Vue as unknown as GlobalAPI
diff --git a/src/core/instance/init.ts b/src/core/instance/init.ts
deleted file mode 100644
index c38eb9f9840..00000000000
--- a/src/core/instance/init.ts
+++ /dev/null
@@ -1,143 +0,0 @@
-import config from '../config'
-import { initProxy } from './proxy'
-import { initState } from './state'
-import { initRender } from './render'
-import { initEvents } from './events'
-import { mark, measure } from '../util/perf'
-import { initLifecycle, callHook } from './lifecycle'
-import { initProvide, initInjections } from './inject'
-import { extend, mergeOptions, formatComponentName } from '../util/index'
-import type { Component } from 'types/component'
-import type { InternalComponentOptions } from 'types/options'
-import { EffectScope } from 'v3/reactivity/effectScope'
-
-let uid = 0
-
-export function initMixin(Vue: typeof Component) {
-  Vue.prototype._init = function (options?: Record<string, any>) {
-    const vm: Component = this
-    // a uid
-    vm._uid = uid++
-
-    let startTag, endTag
-    /* istanbul ignore if */
-    if (__DEV__ && config.performance && mark) {
-      startTag = `vue-perf-start:${vm._uid}`
-      endTag = `vue-perf-end:${vm._uid}`
-      mark(startTag)
-    }
-
-    // a flag to mark this as a Vue instance without having to do instanceof
-    // check
-    vm._isVue = true
-    // avoid instances from being observed
-    vm.__v_skip = true
-    // effect scope
-    vm._scope = new EffectScope(true /* detached */)
-    // #13134 edge case where a child component is manually created during the
-    // render of a parent component
-    vm._scope.parent = undefined
-    vm._scope._vm = true
-    // merge options
-    if (options && options._isComponent) {
-      // optimize internal component instantiation
-      // since dynamic options merging is pretty slow, and none of the
-      // internal component options needs special treatment.
-      initInternalComponent(vm, options as any)
-    } else {
-      vm.$options = mergeOptions(
-        resolveConstructorOptions(vm.constructor as any),
-        options || {},
-        vm
-      )
-    }
-    /* istanbul ignore else */
-    if (__DEV__) {
-      initProxy(vm)
-    } else {
-      vm._renderProxy = vm
-    }
-    // expose real self
-    vm._self = vm
-    initLifecycle(vm)
-    initEvents(vm)
-    initRender(vm)
-    callHook(vm, 'beforeCreate', undefined, false /* setContext */)
-    initInjections(vm) // resolve injections before data/props
-    initState(vm)
-    initProvide(vm) // resolve provide after data/props
-    callHook(vm, 'created')
-
-    /* istanbul ignore if */
-    if (__DEV__ && config.performance && mark) {
-      vm._name = formatComponentName(vm, false)
-      mark(endTag)
-      measure(`vue ${vm._name} init`, startTag, endTag)
-    }
-
-    if (vm.$options.el) {
-      vm.$mount(vm.$options.el)
-    }
-  }
-}
-
-export function initInternalComponent(
-  vm: Component,
-  options: InternalComponentOptions
-) {
-  const opts = (vm.$options = Object.create((vm.constructor as any).options))
-  // doing this because it's faster than dynamic enumeration.
-  const parentVnode = options._parentVnode
-  opts.parent = options.parent
-  opts._parentVnode = parentVnode
-
-  const vnodeComponentOptions = parentVnode.componentOptions!
-  opts.propsData = vnodeComponentOptions.propsData
-  opts._parentListeners = vnodeComponentOptions.listeners
-  opts._renderChildren = vnodeComponentOptions.children
-  opts._componentTag = vnodeComponentOptions.tag
-
-  if (options.render) {
-    opts.render = options.render
-    opts.staticRenderFns = options.staticRenderFns
-  }
-}
-
-export function resolveConstructorOptions(Ctor: typeof Component) {
-  let options = Ctor.options
-  if (Ctor.super) {
-    const superOptions = resolveConstructorOptions(Ctor.super)
-    const cachedSuperOptions = Ctor.superOptions
-    if (superOptions !== cachedSuperOptions) {
-      // super option changed,
-      // need to resolve new options.
-      Ctor.superOptions = superOptions
-      // check if there are any late-modified/attached options (#4976)
-      const modifiedOptions = resolveModifiedOptions(Ctor)
-      // update base extend options
-      if (modifiedOptions) {
-        extend(Ctor.extendOptions, modifiedOptions)
-      }
-      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
-      if (options.name) {
-        options.components[options.name] = Ctor
-      }
-    }
-  }
-  return options
-}
-
-function resolveModifiedOptions(
-  Ctor: typeof Component
-): Record<string, any> | null {
-  let modified
-  const latest = Ctor.options
-  const sealed = Ctor.sealedOptions
-  for (const key in latest) {
-    if (latest[key] !== sealed[key]) {
-      if (!modified) modified = {}
-      modified[key] = latest[key]
-    }
-  }
-  return modified
-}
diff --git a/src/core/instance/inject.ts b/src/core/instance/inject.ts
deleted file mode 100644
index d5fa2b5ba71..00000000000
--- a/src/core/instance/inject.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { warn, hasSymbol, isFunction, isObject } from '../util/index'
-import { defineReactive, toggleObserving } from '../observer/index'
-import type { Component } from 'types/component'
-import { resolveProvided } from 'v3/apiInject'
-
-export function initProvide(vm: Component) {
-  const provideOption = vm.$options.provide
-  if (provideOption) {
-    const provided = isFunction(provideOption)
-      ? provideOption.call(vm)
-      : provideOption
-    if (!isObject(provided)) {
-      return
-    }
-    const source = resolveProvided(vm)
-    // IE9 doesn't support Object.getOwnPropertyDescriptors so we have to
-    // iterate the keys ourselves.
-    const keys = hasSymbol ? Reflect.ownKeys(provided) : Object.keys(provided)
-    for (let i = 0; i < keys.length; i++) {
-      const key = keys[i]
-      Object.defineProperty(
-        source,
-        key,
-        Object.getOwnPropertyDescriptor(provided, key)!
-      )
-    }
-  }
-}
-
-export function initInjections(vm: Component) {
-  const result = resolveInject(vm.$options.inject, vm)
-  if (result) {
-    toggleObserving(false)
-    Object.keys(result).forEach(key => {
-      /* istanbul ignore else */
-      if (__DEV__) {
-        defineReactive(vm, key, result[key], () => {
-          warn(
-            `Avoid mutating an injected value directly since the changes will be ` +
-              `overwritten whenever the provided component re-renders. ` +
-              `injection being mutated: "${key}"`,
-            vm
-          )
-        })
-      } else {
-        defineReactive(vm, key, result[key])
-      }
-    })
-    toggleObserving(true)
-  }
-}
-
-export function resolveInject(
-  inject: any,
-  vm: Component
-): Record<string, any> | undefined | null {
-  if (inject) {
-    // inject is :any because flow is not smart enough to figure out cached
-    const result = Object.create(null)
-    const keys = hasSymbol ? Reflect.ownKeys(inject) : Object.keys(inject)
-
-    for (let i = 0; i < keys.length; i++) {
-      const key = keys[i]
-      // #6574 in case the inject object is observed...
-      if (key === '__ob__') continue
-      const provideKey = inject[key].from
-      if (provideKey in vm._provided) {
-        result[key] = vm._provided[provideKey]
-      } else if ('default' in inject[key]) {
-        const provideDefault = inject[key].default
-        result[key] = isFunction(provideDefault)
-          ? provideDefault.call(vm)
-          : provideDefault
-      } else if (__DEV__) {
-        warn(`Injection "${key as string}" not found`, vm)
-      }
-    }
-    return result
-  }
-}
diff --git a/src/core/instance/lifecycle.ts b/src/core/instance/lifecycle.ts
deleted file mode 100644
index d1b5a76d990..00000000000
--- a/src/core/instance/lifecycle.ts
+++ /dev/null
@@ -1,421 +0,0 @@
-import config from '../config'
-import Watcher, { WatcherOptions } from '../observer/watcher'
-import { mark, measure } from '../util/perf'
-import VNode, { createEmptyVNode } from '../vdom/vnode'
-import { updateComponentListeners } from './events'
-import { resolveSlots } from './render-helpers/resolve-slots'
-import { toggleObserving } from '../observer/index'
-import { pushTarget, popTarget } from '../observer/dep'
-import type { Component } from 'types/component'
-import type { MountedComponentVNode } from 'types/vnode'
-
-import {
-  warn,
-  noop,
-  remove,
-  emptyObject,
-  validateProp,
-  invokeWithErrorHandling
-} from '../util/index'
-import { currentInstance, setCurrentInstance } from 'v3/currentInstance'
-import { getCurrentScope } from 'v3/reactivity/effectScope'
-import { syncSetupProxy } from 'v3/apiSetup'
-
-export let activeInstance: any = null
-export let isUpdatingChildComponent: boolean = false
-
-export function setActiveInstance(vm: Component) {
-  const prevActiveInstance = activeInstance
-  activeInstance = vm
-  return () => {
-    activeInstance = prevActiveInstance
-  }
-}
-
-export function initLifecycle(vm: Component) {
-  const options = vm.$options
-
-  // locate first non-abstract parent
-  let parent = options.parent
-  if (parent && !options.abstract) {
-    while (parent.$options.abstract && parent.$parent) {
-      parent = parent.$parent
-    }
-    parent.$children.push(vm)
-  }
-
-  vm.$parent = parent
-  vm.$root = parent ? parent.$root : vm
-
-  vm.$children = []
-  vm.$refs = {}
-
-  vm._provided = parent ? parent._provided : Object.create(null)
-  vm._watcher = null
-  vm._inactive = null
-  vm._directInactive = false
-  vm._isMounted = false
-  vm._isDestroyed = false
-  vm._isBeingDestroyed = false
-}
-
-export function lifecycleMixin(Vue: typeof Component) {
-  Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
-    const vm: Component = this
-    const prevEl = vm.$el
-    const prevVnode = vm._vnode
-    const restoreActiveInstance = setActiveInstance(vm)
-    vm._vnode = vnode
-    // Vue.prototype.__patch__ is injected in entry points
-    // based on the rendering backend used.
-    if (!prevVnode) {
-      // initial render
-      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
-    } else {
-      // updates
-      vm.$el = vm.__patch__(prevVnode, vnode)
-    }
-    restoreActiveInstance()
-    // update __vue__ reference
-    if (prevEl) {
-      prevEl.__vue__ = null
-    }
-    if (vm.$el) {
-      vm.$el.__vue__ = vm
-    }
-    // if parent is an HOC, update its $el as well
-    let wrapper: Component | undefined = vm
-    while (
-      wrapper &&
-      wrapper.$vnode &&
-      wrapper.$parent &&
-      wrapper.$vnode === wrapper.$parent._vnode
-    ) {
-      wrapper.$parent.$el = wrapper.$el
-      wrapper = wrapper.$parent
-    }
-    // updated hook is called by the scheduler to ensure that children are
-    // updated in a parent's updated hook.
-  }
-
-  Vue.prototype.$forceUpdate = function () {
-    const vm: Component = this
-    if (vm._watcher) {
-      vm._watcher.update()
-    }
-  }
-
-  Vue.prototype.$destroy = function () {
-    const vm: Component = this
-    if (vm._isBeingDestroyed) {
-      return
-    }
-    callHook(vm, 'beforeDestroy')
-    vm._isBeingDestroyed = true
-    // remove self from parent
-    const parent = vm.$parent
-    if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
-      remove(parent.$children, vm)
-    }
-    // teardown scope. this includes both the render watcher and other
-    // watchers created
-    vm._scope.stop()
-    // remove reference from data ob
-    // frozen object may not have observer.
-    if (vm._data.__ob__) {
-      vm._data.__ob__.vmCount--
-    }
-    // call the last hook...
-    vm._isDestroyed = true
-    // invoke destroy hooks on current rendered tree
-    vm.__patch__(vm._vnode, null)
-    // fire destroyed hook
-    callHook(vm, 'destroyed')
-    // turn off all instance listeners.
-    vm.$off()
-    // remove __vue__ reference
-    if (vm.$el) {
-      vm.$el.__vue__ = null
-    }
-    // release circular reference (#6759)
-    if (vm.$vnode) {
-      vm.$vnode.parent = null
-    }
-  }
-}
-
-export function mountComponent(
-  vm: Component,
-  el: Element | null | undefined,
-  hydrating?: boolean
-): Component {
-  vm.$el = el
-  if (!vm.$options.render) {
-    // @ts-expect-error invalid type
-    vm.$options.render = createEmptyVNode
-    if (__DEV__) {
-      /* istanbul ignore if */
-      if (
-        (vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
-        vm.$options.el ||
-        el
-      ) {
-        warn(
-          'You are using the runtime-only build of Vue where the template ' +
-            'compiler is not available. Either pre-compile the templates into ' +
-            'render functions, or use the compiler-included build.',
-          vm
-        )
-      } else {
-        warn(
-          'Failed to mount component: template or render function not defined.',
-          vm
-        )
-      }
-    }
-  }
-  callHook(vm, 'beforeMount')
-
-  let updateComponent
-  /* istanbul ignore if */
-  if (__DEV__ && config.performance && mark) {
-    updateComponent = () => {
-      const name = vm._name
-      const id = vm._uid
-      const startTag = `vue-perf-start:${id}`
-      const endTag = `vue-perf-end:${id}`
-
-      mark(startTag)
-      const vnode = vm._render()
-      mark(endTag)
-      measure(`vue ${name} render`, startTag, endTag)
-
-      mark(startTag)
-      vm._update(vnode, hydrating)
-      mark(endTag)
-      measure(`vue ${name} patch`, startTag, endTag)
-    }
-  } else {
-    updateComponent = () => {
-      vm._update(vm._render(), hydrating)
-    }
-  }
-
-  const watcherOptions: WatcherOptions = {
-    before() {
-      if (vm._isMounted && !vm._isDestroyed) {
-        callHook(vm, 'beforeUpdate')
-      }
-    }
-  }
-
-  if (__DEV__) {
-    watcherOptions.onTrack = e => callHook(vm, 'renderTracked', [e])
-    watcherOptions.onTrigger = e => callHook(vm, 'renderTriggered', [e])
-  }
-
-  // we set this to vm._watcher inside the watcher's constructor
-  // since the watcher's initial patch may call $forceUpdate (e.g. inside child
-  // component's mounted hook), which relies on vm._watcher being already defined
-  new Watcher(
-    vm,
-    updateComponent,
-    noop,
-    watcherOptions,
-    true /* isRenderWatcher */
-  )
-  hydrating = false
-
-  // flush buffer for flush: "pre" watchers queued in setup()
-  const preWatchers = vm._preWatchers
-  if (preWatchers) {
-    for (let i = 0; i < preWatchers.length; i++) {
-      preWatchers[i].run()
-    }
-  }
-
-  // manually mounted instance, call mounted on self
-  // mounted is called for render-created child components in its inserted hook
-  if (vm.$vnode == null) {
-    vm._isMounted = true
-    callHook(vm, 'mounted')
-  }
-  return vm
-}
-
-export function updateChildComponent(
-  vm: Component,
-  propsData: Record<string, any> | null | undefined,
-  listeners: Record<string, Function | Array<Function>> | undefined,
-  parentVnode: MountedComponentVNode,
-  renderChildren?: Array<VNode> | null
-) {
-  if (__DEV__) {
-    isUpdatingChildComponent = true
-  }
-
-  // determine whether component has slot children
-  // we need to do this before overwriting $options._renderChildren.
-
-  // check if there are dynamic scopedSlots (hand-written or compiled but with
-  // dynamic slot names). Static scoped slots compiled from template has the
-  // "$stable" marker.
-  const newScopedSlots = parentVnode.data.scopedSlots
-  const oldScopedSlots = vm.$scopedSlots
-  const hasDynamicScopedSlot = !!(
-    (newScopedSlots && !newScopedSlots.$stable) ||
-    (oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) ||
-    (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key) ||
-    (!newScopedSlots && vm.$scopedSlots.$key)
-  )
-
-  // Any static slot children from the parent may have changed during parent's
-  // update. Dynamic scoped slots may also have changed. In such cases, a forced
-  // update is necessary to ensure correctness.
-  let needsForceUpdate = !!(
-    renderChildren || // has new static slots
-    vm.$options._renderChildren || // has old static slots
-    hasDynamicScopedSlot
-  )
-
-  const prevVNode = vm.$vnode
-  vm.$options._parentVnode = parentVnode
-  vm.$vnode = parentVnode // update vm's placeholder node without re-render
-
-  if (vm._vnode) {
-    // update child tree's parent
-    vm._vnode.parent = parentVnode
-  }
-  vm.$options._renderChildren = renderChildren
-
-  // update $attrs and $listeners hash
-  // these are also reactive so they may trigger child update if the child
-  // used them during render
-  const attrs = parentVnode.data.attrs || emptyObject
-  if (vm._attrsProxy) {
-    // force update if attrs are accessed and has changed since it may be
-    // passed to a child component.
-    if (
-      syncSetupProxy(
-        vm._attrsProxy,
-        attrs,
-        (prevVNode.data && prevVNode.data.attrs) || emptyObject,
-        vm,
-        '$attrs'
-      )
-    ) {
-      needsForceUpdate = true
-    }
-  }
-  vm.$attrs = attrs
-
-  // update listeners
-  listeners = listeners || emptyObject
-  const prevListeners = vm.$options._parentListeners
-  if (vm._listenersProxy) {
-    syncSetupProxy(
-      vm._listenersProxy,
-      listeners,
-      prevListeners || emptyObject,
-      vm,
-      '$listeners'
-    )
-  }
-  vm.$listeners = vm.$options._parentListeners = listeners
-  updateComponentListeners(vm, listeners, prevListeners)
-
-  // update props
-  if (propsData && vm.$options.props) {
-    toggleObserving(false)
-    const props = vm._props
-    const propKeys = vm.$options._propKeys || []
-    for (let i = 0; i < propKeys.length; i++) {
-      const key = propKeys[i]
-      const propOptions: any = vm.$options.props // wtf flow?
-      props[key] = validateProp(key, propOptions, propsData, vm)
-    }
-    toggleObserving(true)
-    // keep a copy of raw propsData
-    vm.$options.propsData = propsData
-  }
-
-  // resolve slots + force update if has children
-  if (needsForceUpdate) {
-    vm.$slots = resolveSlots(renderChildren, parentVnode.context)
-    vm.$forceUpdate()
-  }
-
-  if (__DEV__) {
-    isUpdatingChildComponent = false
-  }
-}
-
-function isInInactiveTree(vm) {
-  while (vm && (vm = vm.$parent)) {
-    if (vm._inactive) return true
-  }
-  return false
-}
-
-export function activateChildComponent(vm: Component, direct?: boolean) {
-  if (direct) {
-    vm._directInactive = false
-    if (isInInactiveTree(vm)) {
-      return
-    }
-  } else if (vm._directInactive) {
-    return
-  }
-  if (vm._inactive || vm._inactive === null) {
-    vm._inactive = false
-    for (let i = 0; i < vm.$children.length; i++) {
-      activateChildComponent(vm.$children[i])
-    }
-    callHook(vm, 'activated')
-  }
-}
-
-export function deactivateChildComponent(vm: Component, direct?: boolean) {
-  if (direct) {
-    vm._directInactive = true
-    if (isInInactiveTree(vm)) {
-      return
-    }
-  }
-  if (!vm._inactive) {
-    vm._inactive = true
-    for (let i = 0; i < vm.$children.length; i++) {
-      deactivateChildComponent(vm.$children[i])
-    }
-    callHook(vm, 'deactivated')
-  }
-}
-
-export function callHook(
-  vm: Component,
-  hook: string,
-  args?: any[],
-  setContext = true
-) {
-  // #7573 disable dep collection when invoking lifecycle hooks
-  pushTarget()
-  const prevInst = currentInstance
-  const prevScope = getCurrentScope()
-  setContext && setCurrentInstance(vm)
-  const handlers = vm.$options[hook]
-  const info = `${hook} hook`
-  if (handlers) {
-    for (let i = 0, j = handlers.length; i < j; i++) {
-      invokeWithErrorHandling(handlers[i], vm, args || null, vm, info)
-    }
-  }
-  if (vm._hasHookEvent) {
-    vm.$emit('hook:' + hook)
-  }
-  if (setContext) {
-    setCurrentInstance(prevInst)
-    prevScope && prevScope.on()
-  }
-
-  popTarget()
-}
diff --git a/src/core/instance/proxy.ts b/src/core/instance/proxy.ts
deleted file mode 100644
index 685d9651fcc..00000000000
--- a/src/core/instance/proxy.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-/* not type checking this file because flow doesn't play well with Proxy */
-
-import config from 'core/config'
-import { warn, makeMap, isNative } from '../util/index'
-
-let initProxy
-
-if (__DEV__) {
-  const allowedGlobals = makeMap(
-    'Infinity,undefined,NaN,isFinite,isNaN,' +
-      'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
-      'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,' +
-      'require' // for Webpack/Browserify
-  )
-
-  const warnNonPresent = (target, key) => {
-    warn(
-      `Property or method "${key}" is not defined on the instance but ` +
-        'referenced during render. Make sure that this property is reactive, ' +
-        'either in the data option, or for class-based components, by ' +
-        'initializing the property. ' +
-        'See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',
-      target
-    )
-  }
-
-  const warnReservedPrefix = (target, key) => {
-    warn(
-      `Property "${key}" must be accessed with "$data.${key}" because ` +
-        'properties starting with "$" or "_" are not proxied in the Vue instance to ' +
-        'prevent conflicts with Vue internals. ' +
-        'See: https://v2.vuejs.org/v2/api/#data',
-      target
-    )
-  }
-
-  const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy)
-
-  if (hasProxy) {
-    const isBuiltInModifier = makeMap(
-      'stop,prevent,self,ctrl,shift,alt,meta,exact'
-    )
-    config.keyCodes = new Proxy(config.keyCodes, {
-      set(target, key: string, value) {
-        if (isBuiltInModifier(key)) {
-          warn(
-            `Avoid overwriting built-in modifier in config.keyCodes: .${key}`
-          )
-          return false
-        } else {
-          target[key] = value
-          return true
-        }
-      }
-    })
-  }
-
-  const hasHandler = {
-    has(target, key) {
-      const has = key in target
-      const isAllowed =
-        allowedGlobals(key) ||
-        (typeof key === 'string' &&
-          key.charAt(0) === '_' &&
-          !(key in target.$data))
-      if (!has && !isAllowed) {
-        if (key in target.$data) warnReservedPrefix(target, key)
-        else warnNonPresent(target, key)
-      }
-      return has || !isAllowed
-    }
-  }
-
-  const getHandler = {
-    get(target, key) {
-      if (typeof key === 'string' && !(key in target)) {
-        if (key in target.$data) warnReservedPrefix(target, key)
-        else warnNonPresent(target, key)
-      }
-      return target[key]
-    }
-  }
-
-  initProxy = function initProxy(vm) {
-    if (hasProxy) {
-      // determine which proxy handler to use
-      const options = vm.$options
-      const handlers =
-        options.render && options.render._withStripped ? getHandler : hasHandler
-      vm._renderProxy = new Proxy(vm, handlers)
-    } else {
-      vm._renderProxy = vm
-    }
-  }
-}
-
-export { initProxy }
diff --git a/src/core/instance/render-helpers/bind-dynamic-keys.ts b/src/core/instance/render-helpers/bind-dynamic-keys.ts
deleted file mode 100644
index a1fefa65816..00000000000
--- a/src/core/instance/render-helpers/bind-dynamic-keys.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-// helper to process dynamic keys for dynamic arguments in v-bind and v-on.
-// For example, the following template:
-//
-// <div id="app" :[key]="value">
-//
-// compiles to the following:
-//
-// _c('div', { attrs: bindDynamicKeys({ "id": "app" }, [key, value]) })
-
-import { warn } from 'core/util/debug'
-
-export function bindDynamicKeys(
-  baseObj: Record<string, any>,
-  values: Array<any>
-): Object {
-  for (let i = 0; i < values.length; i += 2) {
-    const key = values[i]
-    if (typeof key === 'string' && key) {
-      baseObj[values[i]] = values[i + 1]
-    } else if (__DEV__ && key !== '' && key !== null) {
-      // null is a special value for explicitly removing a binding
-      warn(
-        `Invalid value for dynamic directive argument (expected string or null): ${key}`,
-        this
-      )
-    }
-  }
-  return baseObj
-}
-
-// helper to dynamically append modifier runtime markers to event names.
-// ensure only append when value is already string, otherwise it will be cast
-// to string and cause the type check to miss.
-export function prependModifier(value: any, symbol: string): any {
-  return typeof value === 'string' ? symbol + value : value
-}
diff --git a/src/core/instance/render-helpers/bind-object-listeners.ts b/src/core/instance/render-helpers/bind-object-listeners.ts
deleted file mode 100644
index 0cdd8cafcf9..00000000000
--- a/src/core/instance/render-helpers/bind-object-listeners.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { warn, extend, isPlainObject } from 'core/util/index'
-import type { VNodeData } from 'types/vnode'
-
-export function bindObjectListeners(data: any, value: any): VNodeData {
-  if (value) {
-    if (!isPlainObject(value)) {
-      __DEV__ && warn('v-on without argument expects an Object value', this)
-    } else {
-      const on = (data.on = data.on ? extend({}, data.on) : {})
-      for (const key in value) {
-        const existing = on[key]
-        const ours = value[key]
-        on[key] = existing ? [].concat(existing, ours) : ours
-      }
-    }
-  }
-  return data
-}
diff --git a/src/core/instance/render-helpers/bind-object-props.ts b/src/core/instance/render-helpers/bind-object-props.ts
deleted file mode 100644
index d9009a45dce..00000000000
--- a/src/core/instance/render-helpers/bind-object-props.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import config from 'core/config'
-
-import {
-  warn,
-  isObject,
-  toObject,
-  isReservedAttribute,
-  camelize,
-  hyphenate,
-  isArray
-} from 'core/util/index'
-import type { VNodeData } from 'types/vnode'
-
-/**
- * Runtime helper for merging v-bind="object" into a VNode's data.
- */
-export function bindObjectProps(
-  data: any,
-  tag: string,
-  value: any,
-  asProp: boolean,
-  isSync?: boolean
-): VNodeData {
-  if (value) {
-    if (!isObject(value)) {
-      __DEV__ &&
-        warn('v-bind without argument expects an Object or Array value', this)
-    } else {
-      if (isArray(value)) {
-        value = toObject(value)
-      }
-      let hash
-      for (const key in value) {
-        if (key === 'class' || key === 'style' || isReservedAttribute(key)) {
-          hash = data
-        } else {
-          const type = data.attrs && data.attrs.type
-          hash =
-            asProp || config.mustUseProp(tag, type, key)
-              ? data.domProps || (data.domProps = {})
-              : data.attrs || (data.attrs = {})
-        }
-        const camelizedKey = camelize(key)
-        const hyphenatedKey = hyphenate(key)
-        if (!(camelizedKey in hash) && !(hyphenatedKey in hash)) {
-          hash[key] = value[key]
-
-          if (isSync) {
-            const on = data.on || (data.on = {})
-            on[`update:${key}`] = function ($event) {
-              value[key] = $event
-            }
-          }
-        }
-      }
-    }
-  }
-  return data
-}
diff --git a/src/core/instance/render-helpers/check-keycodes.ts b/src/core/instance/render-helpers/check-keycodes.ts
deleted file mode 100644
index 482051ffd19..00000000000
--- a/src/core/instance/render-helpers/check-keycodes.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import config from 'core/config'
-import { hyphenate, isArray } from 'shared/util'
-
-function isKeyNotMatch<T>(expect: T | Array<T>, actual: T): boolean {
-  if (isArray(expect)) {
-    return expect.indexOf(actual) === -1
-  } else {
-    return expect !== actual
-  }
-}
-
-/**
- * Runtime helper for checking keyCodes from config.
- * exposed as Vue.prototype._k
- * passing in eventKeyName as last argument separately for backwards compat
- */
-export function checkKeyCodes(
-  eventKeyCode: number,
-  key: string,
-  builtInKeyCode?: number | Array<number>,
-  eventKeyName?: string,
-  builtInKeyName?: string | Array<string>
-): boolean | null | undefined {
-  const mappedKeyCode = config.keyCodes[key] || builtInKeyCode
-  if (builtInKeyName && eventKeyName && !config.keyCodes[key]) {
-    return isKeyNotMatch(builtInKeyName, eventKeyName)
-  } else if (mappedKeyCode) {
-    return isKeyNotMatch(mappedKeyCode, eventKeyCode)
-  } else if (eventKeyName) {
-    return hyphenate(eventKeyName) !== key
-  }
-  return eventKeyCode === undefined
-}
diff --git a/src/core/instance/render-helpers/index.ts b/src/core/instance/render-helpers/index.ts
deleted file mode 100644
index 27ea60e9c8e..00000000000
--- a/src/core/instance/render-helpers/index.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { toNumber, toString, looseEqual, looseIndexOf } from 'shared/util'
-import { createTextVNode, createEmptyVNode } from 'core/vdom/vnode'
-import { renderList } from './render-list'
-import { renderSlot } from './render-slot'
-import { resolveFilter } from './resolve-filter'
-import { checkKeyCodes } from './check-keycodes'
-import { bindObjectProps } from './bind-object-props'
-import { renderStatic, markOnce } from './render-static'
-import { bindObjectListeners } from './bind-object-listeners'
-import { resolveScopedSlots } from './resolve-scoped-slots'
-import { bindDynamicKeys, prependModifier } from './bind-dynamic-keys'
-
-export function installRenderHelpers(target: any) {
-  target._o = markOnce
-  target._n = toNumber
-  target._s = toString
-  target._l = renderList
-  target._t = renderSlot
-  target._q = looseEqual
-  target._i = looseIndexOf
-  target._m = renderStatic
-  target._f = resolveFilter
-  target._k = checkKeyCodes
-  target._b = bindObjectProps
-  target._v = createTextVNode
-  target._e = createEmptyVNode
-  target._u = resolveScopedSlots
-  target._g = bindObjectListeners
-  target._d = bindDynamicKeys
-  target._p = prependModifier
-}
diff --git a/src/core/instance/render-helpers/render-list.ts b/src/core/instance/render-helpers/render-list.ts
deleted file mode 100644
index ba9318d3ded..00000000000
--- a/src/core/instance/render-helpers/render-list.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { isObject, isDef, hasSymbol, isArray } from 'core/util/index'
-import VNode from 'core/vdom/vnode'
-
-/**
- * Runtime helper for rendering v-for lists.
- */
-export function renderList(
-  val: any,
-  render: (val: any, keyOrIndex: string | number, index?: number) => VNode
-): Array<VNode> | null {
-  let ret: Array<VNode> | null = null,
-    i,
-    l,
-    keys,
-    key
-  if (isArray(val) || typeof val === 'string') {
-    ret = new Array(val.length)
-    for (i = 0, l = val.length; i < l; i++) {
-      ret[i] = render(val[i], i)
-    }
-  } else if (typeof val === 'number') {
-    ret = new Array(val)
-    for (i = 0; i < val; i++) {
-      ret[i] = render(i + 1, i)
-    }
-  } else if (isObject(val)) {
-    if (hasSymbol && val[Symbol.iterator]) {
-      ret = []
-      const iterator: Iterator<any> = val[Symbol.iterator]()
-      let result = iterator.next()
-      while (!result.done) {
-        ret.push(render(result.value, ret.length))
-        result = iterator.next()
-      }
-    } else {
-      keys = Object.keys(val)
-      ret = new Array(keys.length)
-      for (i = 0, l = keys.length; i < l; i++) {
-        key = keys[i]
-        ret[i] = render(val[key], key, i)
-      }
-    }
-  }
-  if (!isDef(ret)) {
-    ret = []
-  }
-  ;(ret as any)._isVList = true
-  return ret
-}
diff --git a/src/core/instance/render-helpers/render-slot.ts b/src/core/instance/render-helpers/render-slot.ts
deleted file mode 100644
index 501143ee3f5..00000000000
--- a/src/core/instance/render-helpers/render-slot.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { extend, warn, isObject, isFunction } from 'core/util/index'
-import VNode from 'core/vdom/vnode'
-
-/**
- * Runtime helper for rendering <slot>
- */
-export function renderSlot(
-  name: string,
-  fallbackRender: ((() => Array<VNode>) | Array<VNode>) | null,
-  props: Record<string, any> | null,
-  bindObject: object | null
-): Array<VNode> | null {
-  const scopedSlotFn = this.$scopedSlots[name]
-  let nodes
-  if (scopedSlotFn) {
-    // scoped slot
-    props = props || {}
-    if (bindObject) {
-      if (__DEV__ && !isObject(bindObject)) {
-        warn('slot v-bind without argument expects an Object', this)
-      }
-      props = extend(extend({}, bindObject), props)
-    }
-    nodes =
-      scopedSlotFn(props) ||
-      (isFunction(fallbackRender) ? fallbackRender() : fallbackRender)
-  } else {
-    nodes =
-      this.$slots[name] ||
-      (isFunction(fallbackRender) ? fallbackRender() : fallbackRender)
-  }
-
-  const target = props && props.slot
-  if (target) {
-    return this.$createElement('template', { slot: target }, nodes)
-  } else {
-    return nodes
-  }
-}
diff --git a/src/core/instance/render-helpers/render-static.ts b/src/core/instance/render-helpers/render-static.ts
deleted file mode 100644
index 9de7ab7ec70..00000000000
--- a/src/core/instance/render-helpers/render-static.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import VNode from 'core/vdom/vnode'
-import { isArray } from 'core/util'
-
-/**
- * Runtime helper for rendering static trees.
- */
-export function renderStatic(
-  index: number,
-  isInFor: boolean
-): VNode | Array<VNode> {
-  const cached = this._staticTrees || (this._staticTrees = [])
-  let tree = cached[index]
-  // if has already-rendered static tree and not inside v-for,
-  // we can reuse the same tree.
-  if (tree && !isInFor) {
-    return tree
-  }
-  // otherwise, render a fresh tree.
-  tree = cached[index] = this.$options.staticRenderFns[index].call(
-    this._renderProxy,
-    this._c,
-    this // for render fns generated for functional component templates
-  )
-  markStatic(tree, `__static__${index}`, false)
-  return tree
-}
-
-/**
- * Runtime helper for v-once.
- * Effectively it means marking the node as static with a unique key.
- */
-export function markOnce(
-  tree: VNode | Array<VNode>,
-  index: number,
-  key: string
-) {
-  markStatic(tree, `__once__${index}${key ? `_${key}` : ``}`, true)
-  return tree
-}
-
-function markStatic(tree: VNode | Array<VNode>, key: string, isOnce: boolean) {
-  if (isArray(tree)) {
-    for (let i = 0; i < tree.length; i++) {
-      if (tree[i] && typeof tree[i] !== 'string') {
-        markStaticNode(tree[i], `${key}_${i}`, isOnce)
-      }
-    }
-  } else {
-    markStaticNode(tree, key, isOnce)
-  }
-}
-
-function markStaticNode(node, key, isOnce) {
-  node.isStatic = true
-  node.key = key
-  node.isOnce = isOnce
-}
diff --git a/src/core/instance/render-helpers/resolve-filter.ts b/src/core/instance/render-helpers/resolve-filter.ts
deleted file mode 100644
index c9de5ce9c60..00000000000
--- a/src/core/instance/render-helpers/resolve-filter.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { identity, resolveAsset } from 'core/util/index'
-
-/**
- * Runtime helper for resolving filters
- */
-export function resolveFilter(id: string): Function {
-  return resolveAsset(this.$options, 'filters', id, true) || identity
-}
diff --git a/src/core/instance/render-helpers/resolve-scoped-slots.ts b/src/core/instance/render-helpers/resolve-scoped-slots.ts
deleted file mode 100644
index 6a25d084a36..00000000000
--- a/src/core/instance/render-helpers/resolve-scoped-slots.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import type { ScopedSlotsData } from 'types/vnode'
-import { isArray } from 'core/util'
-
-export function resolveScopedSlots(
-  fns: ScopedSlotsData,
-  res?: Record<string, any>,
-  // the following are added in 2.6
-  hasDynamicKeys?: boolean,
-  contentHashKey?: number
-): { $stable: boolean } & { [key: string]: Function } {
-  res = res || { $stable: !hasDynamicKeys }
-  for (let i = 0; i < fns.length; i++) {
-    const slot = fns[i]
-    if (isArray(slot)) {
-      resolveScopedSlots(slot, res, hasDynamicKeys)
-    } else if (slot) {
-      // marker for reverse proxying v-slot without scope on this.$slots
-      // @ts-expect-error
-      if (slot.proxy) {
-        // @ts-expect-error
-        slot.fn.proxy = true
-      }
-      res[slot.key] = slot.fn
-    }
-  }
-  if (contentHashKey) {
-    ;(res as any).$key = contentHashKey
-  }
-  return res as any
-}
diff --git a/src/core/instance/render-helpers/resolve-slots.ts b/src/core/instance/render-helpers/resolve-slots.ts
deleted file mode 100644
index 5536f919145..00000000000
--- a/src/core/instance/render-helpers/resolve-slots.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import type VNode from 'core/vdom/vnode'
-import type { Component } from 'types/component'
-
-/**
- * Runtime helper for resolving raw children VNodes into a slot object.
- */
-export function resolveSlots(
-  children: Array<VNode> | null | undefined,
-  context: Component | null
-): { [key: string]: Array<VNode> } {
-  if (!children || !children.length) {
-    return {}
-  }
-  const slots: Record<string, any> = {}
-  for (let i = 0, l = children.length; i < l; i++) {
-    const child = children[i]
-    const data = child.data
-    // remove slot attribute if the node is resolved as a Vue slot node
-    if (data && data.attrs && data.attrs.slot) {
-      delete data.attrs.slot
-    }
-    // named slots should only be respected if the vnode was rendered in the
-    // same context.
-    if (
-      (child.context === context || child.fnContext === context) &&
-      data &&
-      data.slot != null
-    ) {
-      const name = data.slot
-      const slot = slots[name] || (slots[name] = [])
-      if (child.tag === 'template') {
-        slot.push.apply(slot, child.children || [])
-      } else {
-        slot.push(child)
-      }
-    } else {
-      ;(slots.default || (slots.default = [])).push(child)
-    }
-  }
-  // ignore slots that contains only whitespace
-  for (const name in slots) {
-    if (slots[name].every(isWhitespace)) {
-      delete slots[name]
-    }
-  }
-  return slots
-}
-
-function isWhitespace(node: VNode): boolean {
-  return (node.isComment && !node.asyncFactory) || node.text === ' '
-}
diff --git a/src/core/instance/render.ts b/src/core/instance/render.ts
deleted file mode 100644
index 6553f217480..00000000000
--- a/src/core/instance/render.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-import {
-  warn,
-  nextTick,
-  emptyObject,
-  handleError,
-  defineReactive,
-  isArray
-} from '../util/index'
-
-import { createElement } from '../vdom/create-element'
-import { installRenderHelpers } from './render-helpers/index'
-import { resolveSlots } from './render-helpers/resolve-slots'
-import { normalizeScopedSlots } from '../vdom/helpers/normalize-scoped-slots'
-import VNode, { createEmptyVNode } from '../vdom/vnode'
-
-import { isUpdatingChildComponent } from './lifecycle'
-import type { Component } from 'types/component'
-import { currentInstance, setCurrentInstance } from 'v3/currentInstance'
-import { syncSetupSlots } from 'v3/apiSetup'
-
-export function initRender(vm: Component) {
-  vm._vnode = null // the root of the child tree
-  vm._staticTrees = null // v-once cached trees
-  const options = vm.$options
-  const parentVnode = (vm.$vnode = options._parentVnode!) // the placeholder node in parent tree
-  const renderContext = parentVnode && (parentVnode.context as Component)
-  vm.$slots = resolveSlots(options._renderChildren, renderContext)
-  vm.$scopedSlots = parentVnode
-    ? normalizeScopedSlots(
-        vm.$parent!,
-        parentVnode.data!.scopedSlots,
-        vm.$slots
-      )
-    : emptyObject
-  // bind the createElement fn to this instance
-  // so that we get proper render context inside it.
-  // args order: tag, data, children, normalizationType, alwaysNormalize
-  // internal version is used by render functions compiled from templates
-  // @ts-expect-error
-  vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
-  // normalization is always applied for the public version, used in
-  // user-written render functions.
-  // @ts-expect-error
-  vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
-
-  // $attrs & $listeners are exposed for easier HOC creation.
-  // they need to be reactive so that HOCs using them are always updated
-  const parentData = parentVnode && parentVnode.data
-
-  /* istanbul ignore else */
-  if (__DEV__) {
-    defineReactive(
-      vm,
-      '$attrs',
-      (parentData && parentData.attrs) || emptyObject,
-      () => {
-        !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
-      },
-      true
-    )
-    defineReactive(
-      vm,
-      '$listeners',
-      options._parentListeners || emptyObject,
-      () => {
-        !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
-      },
-      true
-    )
-  } else {
-    defineReactive(
-      vm,
-      '$attrs',
-      (parentData && parentData.attrs) || emptyObject,
-      null,
-      true
-    )
-    defineReactive(
-      vm,
-      '$listeners',
-      options._parentListeners || emptyObject,
-      null,
-      true
-    )
-  }
-}
-
-export let currentRenderingInstance: Component | null = null
-
-// for testing only
-export function setCurrentRenderingInstance(vm: Component) {
-  currentRenderingInstance = vm
-}
-
-export function renderMixin(Vue: typeof Component) {
-  // install runtime convenience helpers
-  installRenderHelpers(Vue.prototype)
-
-  Vue.prototype.$nextTick = function (fn: (...args: any[]) => any) {
-    return nextTick(fn, this)
-  }
-
-  Vue.prototype._render = function (): VNode {
-    const vm: Component = this
-    const { render, _parentVnode } = vm.$options
-
-    if (_parentVnode && vm._isMounted) {
-      vm.$scopedSlots = normalizeScopedSlots(
-        vm.$parent!,
-        _parentVnode.data!.scopedSlots,
-        vm.$slots,
-        vm.$scopedSlots
-      )
-      if (vm._slotsProxy) {
-        syncSetupSlots(vm._slotsProxy, vm.$scopedSlots)
-      }
-    }
-
-    // set parent vnode. this allows render functions to have access
-    // to the data on the placeholder node.
-    vm.$vnode = _parentVnode!
-    // render self
-    const prevInst = currentInstance
-    const prevRenderInst = currentRenderingInstance
-    let vnode
-    try {
-      setCurrentInstance(vm)
-      currentRenderingInstance = vm
-      vnode = render.call(vm._renderProxy, vm.$createElement)
-    } catch (e: any) {
-      handleError(e, vm, `render`)
-      // return error render result,
-      // or previous vnode to prevent render error causing blank component
-      /* istanbul ignore else */
-      if (__DEV__ && vm.$options.renderError) {
-        try {
-          vnode = vm.$options.renderError.call(
-            vm._renderProxy,
-            vm.$createElement,
-            e
-          )
-        } catch (e: any) {
-          handleError(e, vm, `renderError`)
-          vnode = vm._vnode
-        }
-      } else {
-        vnode = vm._vnode
-      }
-    } finally {
-      currentRenderingInstance = prevRenderInst
-      setCurrentInstance(prevInst)
-    }
-    // if the returned array contains only a single node, allow it
-    if (isArray(vnode) && vnode.length === 1) {
-      vnode = vnode[0]
-    }
-    // return empty vnode in case the render function errored out
-    if (!(vnode instanceof VNode)) {
-      if (__DEV__ && isArray(vnode)) {
-        warn(
-          'Multiple root nodes returned from render function. Render function ' +
-            'should return a single root node.',
-          vm
-        )
-      }
-      vnode = createEmptyVNode()
-    }
-    // set parent
-    vnode.parent = _parentVnode
-    return vnode
-  }
-}
diff --git a/src/core/instance/state.ts b/src/core/instance/state.ts
deleted file mode 100644
index f59dda13dd2..00000000000
--- a/src/core/instance/state.ts
+++ /dev/null
@@ -1,393 +0,0 @@
-import config from '../config'
-import Watcher from '../observer/watcher'
-import Dep, { pushTarget, popTarget } from '../observer/dep'
-import { isUpdatingChildComponent } from './lifecycle'
-import { initSetup } from 'v3/apiSetup'
-
-import {
-  set,
-  del,
-  observe,
-  defineReactive,
-  toggleObserving
-} from '../observer/index'
-
-import {
-  warn,
-  bind,
-  noop,
-  hasOwn,
-  isArray,
-  hyphenate,
-  isReserved,
-  handleError,
-  nativeWatch,
-  validateProp,
-  isPlainObject,
-  isServerRendering,
-  isReservedAttribute,
-  invokeWithErrorHandling,
-  isFunction
-} from '../util/index'
-import type { Component } from 'types/component'
-import { shallowReactive, TrackOpTypes } from 'v3'
-
-const sharedPropertyDefinition = {
-  enumerable: true,
-  configurable: true,
-  get: noop,
-  set: noop
-}
-
-export function proxy(target: Object, sourceKey: string, key: string) {
-  sharedPropertyDefinition.get = function proxyGetter() {
-    return this[sourceKey][key]
-  }
-  sharedPropertyDefinition.set = function proxySetter(val) {
-    this[sourceKey][key] = val
-  }
-  Object.defineProperty(target, key, sharedPropertyDefinition)
-}
-
-export function initState(vm: Component) {
-  const opts = vm.$options
-  if (opts.props) initProps(vm, opts.props)
-
-  // Composition API
-  initSetup(vm)
-
-  if (opts.methods) initMethods(vm, opts.methods)
-  if (opts.data) {
-    initData(vm)
-  } else {
-    const ob = observe((vm._data = {}))
-    ob && ob.vmCount++
-  }
-  if (opts.computed) initComputed(vm, opts.computed)
-  if (opts.watch && opts.watch !== nativeWatch) {
-    initWatch(vm, opts.watch)
-  }
-}
-
-function initProps(vm: Component, propsOptions: Object) {
-  const propsData = vm.$options.propsData || {}
-  const props = (vm._props = shallowReactive({}))
-  // cache prop keys so that future props updates can iterate using Array
-  // instead of dynamic object key enumeration.
-  const keys: string[] = (vm.$options._propKeys = [])
-  const isRoot = !vm.$parent
-  // root instance props should be converted
-  if (!isRoot) {
-    toggleObserving(false)
-  }
-  for (const key in propsOptions) {
-    keys.push(key)
-    const value = validateProp(key, propsOptions, propsData, vm)
-    /* istanbul ignore else */
-    if (__DEV__) {
-      const hyphenatedKey = hyphenate(key)
-      if (
-        isReservedAttribute(hyphenatedKey) ||
-        config.isReservedAttr(hyphenatedKey)
-      ) {
-        warn(
-          `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
-          vm
-        )
-      }
-      defineReactive(
-        props,
-        key,
-        value,
-        () => {
-          if (!isRoot && !isUpdatingChildComponent) {
-            warn(
-              `Avoid mutating a prop directly since the value will be ` +
-                `overwritten whenever the parent component re-renders. ` +
-                `Instead, use a data or computed property based on the prop's ` +
-                `value. Prop being mutated: "${key}"`,
-              vm
-            )
-          }
-        },
-        true /* shallow */
-      )
-    } else {
-      defineReactive(props, key, value, undefined, true /* shallow */)
-    }
-    // static props are already proxied on the component's prototype
-    // during Vue.extend(). We only need to proxy props defined at
-    // instantiation here.
-    if (!(key in vm)) {
-      proxy(vm, `_props`, key)
-    }
-  }
-  toggleObserving(true)
-}
-
-function initData(vm: Component) {
-  let data: any = vm.$options.data
-  data = vm._data = isFunction(data) ? getData(data, vm) : data || {}
-  if (!isPlainObject(data)) {
-    data = {}
-    __DEV__ &&
-      warn(
-        'data functions should return an object:\n' +
-          'https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
-        vm
-      )
-  }
-  // proxy data on instance
-  const keys = Object.keys(data)
-  const props = vm.$options.props
-  const methods = vm.$options.methods
-  let i = keys.length
-  while (i--) {
-    const key = keys[i]
-    if (__DEV__) {
-      if (methods && hasOwn(methods, key)) {
-        warn(`Method "${key}" has already been defined as a data property.`, vm)
-      }
-    }
-    if (props && hasOwn(props, key)) {
-      __DEV__ &&
-        warn(
-          `The data property "${key}" is already declared as a prop. ` +
-            `Use prop default value instead.`,
-          vm
-        )
-    } else if (!isReserved(key)) {
-      proxy(vm, `_data`, key)
-    }
-  }
-  // observe data
-  const ob = observe(data)
-  ob && ob.vmCount++
-}
-
-export function getData(data: Function, vm: Component): any {
-  // #7573 disable dep collection when invoking data getters
-  pushTarget()
-  try {
-    return data.call(vm, vm)
-  } catch (e: any) {
-    handleError(e, vm, `data()`)
-    return {}
-  } finally {
-    popTarget()
-  }
-}
-
-const computedWatcherOptions = { lazy: true }
-
-function initComputed(vm: Component, computed: Object) {
-  // $flow-disable-line
-  const watchers = (vm._computedWatchers = Object.create(null))
-  // computed properties are just getters during SSR
-  const isSSR = isServerRendering()
-
-  for (const key in computed) {
-    const userDef = computed[key]
-    const getter = isFunction(userDef) ? userDef : userDef.get
-    if (__DEV__ && getter == null) {
-      warn(`Getter is missing for computed property "${key}".`, vm)
-    }
-
-    if (!isSSR) {
-      // create internal watcher for the computed property.
-      watchers[key] = new Watcher(
-        vm,
-        getter || noop,
-        noop,
-        computedWatcherOptions
-      )
-    }
-
-    // component-defined computed properties are already defined on the
-    // component prototype. We only need to define computed properties defined
-    // at instantiation here.
-    if (!(key in vm)) {
-      defineComputed(vm, key, userDef)
-    } else if (__DEV__) {
-      if (key in vm.$data) {
-        warn(`The computed property "${key}" is already defined in data.`, vm)
-      } else if (vm.$options.props && key in vm.$options.props) {
-        warn(`The computed property "${key}" is already defined as a prop.`, vm)
-      } else if (vm.$options.methods && key in vm.$options.methods) {
-        warn(
-          `The computed property "${key}" is already defined as a method.`,
-          vm
-        )
-      }
-    }
-  }
-}
-
-export function defineComputed(
-  target: any,
-  key: string,
-  userDef: Record<string, any> | (() => any)
-) {
-  const shouldCache = !isServerRendering()
-  if (isFunction(userDef)) {
-    sharedPropertyDefinition.get = shouldCache
-      ? createComputedGetter(key)
-      : createGetterInvoker(userDef)
-    sharedPropertyDefinition.set = noop
-  } else {
-    sharedPropertyDefinition.get = userDef.get
-      ? shouldCache && userDef.cache !== false
-        ? createComputedGetter(key)
-        : createGetterInvoker(userDef.get)
-      : noop
-    sharedPropertyDefinition.set = userDef.set || noop
-  }
-  if (__DEV__ && sharedPropertyDefinition.set === noop) {
-    sharedPropertyDefinition.set = function () {
-      warn(
-        `Computed property "${key}" was assigned to but it has no setter.`,
-        this
-      )
-    }
-  }
-  Object.defineProperty(target, key, sharedPropertyDefinition)
-}
-
-function createComputedGetter(key) {
-  return function computedGetter() {
-    const watcher = this._computedWatchers && this._computedWatchers[key]
-    if (watcher) {
-      if (watcher.dirty) {
-        watcher.evaluate()
-      }
-      if (Dep.target) {
-        if (__DEV__ && Dep.target.onTrack) {
-          Dep.target.onTrack({
-            effect: Dep.target,
-            target: this,
-            type: TrackOpTypes.GET,
-            key
-          })
-        }
-        watcher.depend()
-      }
-      return watcher.value
-    }
-  }
-}
-
-function createGetterInvoker(fn) {
-  return function computedGetter() {
-    return fn.call(this, this)
-  }
-}
-
-function initMethods(vm: Component, methods: Object) {
-  const props = vm.$options.props
-  for (const key in methods) {
-    if (__DEV__) {
-      if (typeof methods[key] !== 'function') {
-        warn(
-          `Method "${key}" has type "${typeof methods[
-            key
-          ]}" in the component definition. ` +
-            `Did you reference the function correctly?`,
-          vm
-        )
-      }
-      if (props && hasOwn(props, key)) {
-        warn(`Method "${key}" has already been defined as a prop.`, vm)
-      }
-      if (key in vm && isReserved(key)) {
-        warn(
-          `Method "${key}" conflicts with an existing Vue instance method. ` +
-            `Avoid defining component methods that start with _ or $.`
-        )
-      }
-    }
-    vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
-  }
-}
-
-function initWatch(vm: Component, watch: Object) {
-  for (const key in watch) {
-    const handler = watch[key]
-    if (isArray(handler)) {
-      for (let i = 0; i < handler.length; i++) {
-        createWatcher(vm, key, handler[i])
-      }
-    } else {
-      createWatcher(vm, key, handler)
-    }
-  }
-}
-
-function createWatcher(
-  vm: Component,
-  expOrFn: string | (() => any),
-  handler: any,
-  options?: Object
-) {
-  if (isPlainObject(handler)) {
-    options = handler
-    handler = handler.handler
-  }
-  if (typeof handler === 'string') {
-    handler = vm[handler]
-  }
-  return vm.$watch(expOrFn, handler, options)
-}
-
-export function stateMixin(Vue: typeof Component) {
-  // flow somehow has problems with directly declared definition object
-  // when using Object.defineProperty, so we have to procedurally build up
-  // the object here.
-  const dataDef: any = {}
-  dataDef.get = function () {
-    return this._data
-  }
-  const propsDef: any = {}
-  propsDef.get = function () {
-    return this._props
-  }
-  if (__DEV__) {
-    dataDef.set = function () {
-      warn(
-        'Avoid replacing instance root $data. ' +
-          'Use nested data properties instead.',
-        this
-      )
-    }
-    propsDef.set = function () {
-      warn(`$props is readonly.`, this)
-    }
-  }
-  Object.defineProperty(Vue.prototype, '$data', dataDef)
-  Object.defineProperty(Vue.prototype, '$props', propsDef)
-
-  Vue.prototype.$set = set
-  Vue.prototype.$delete = del
-
-  Vue.prototype.$watch = function (
-    expOrFn: string | (() => any),
-    cb: any,
-    options?: Record<string, any>
-  ): Function {
-    const vm: Component = this
-    if (isPlainObject(cb)) {
-      return createWatcher(vm, expOrFn, cb, options)
-    }
-    options = options || {}
-    options.user = true
-    const watcher = new Watcher(vm, expOrFn, cb, options)
-    if (options.immediate) {
-      const info = `callback for immediate watcher "${watcher.expression}"`
-      pushTarget()
-      invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
-      popTarget()
-    }
-    return function unwatchFn() {
-      watcher.teardown()
-    }
-  }
-}
diff --git a/src/core/observer/array.ts b/src/core/observer/array.ts
deleted file mode 100644
index b2ff2385555..00000000000
--- a/src/core/observer/array.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * not type checking this file because flow doesn't play well with
- * dynamically accessing methods on Array prototype
- */
-
-import { TriggerOpTypes } from '../../v3'
-import { def } from '../util/index'
-
-const arrayProto = Array.prototype
-export const arrayMethods = Object.create(arrayProto)
-
-const methodsToPatch = [
-  'push',
-  'pop',
-  'shift',
-  'unshift',
-  'splice',
-  'sort',
-  'reverse'
-]
-
-/**
- * Intercept mutating methods and emit events
- */
-methodsToPatch.forEach(function (method) {
-  // cache original method
-  const original = arrayProto[method]
-  def(arrayMethods, method, function mutator(...args) {
-    const result = original.apply(this, args)
-    const ob = this.__ob__
-    let inserted
-    switch (method) {
-      case 'push':
-      case 'unshift':
-        inserted = args
-        break
-      case 'splice':
-        inserted = args.slice(2)
-        break
-    }
-    if (inserted) ob.observeArray(inserted)
-    // notify change
-    if (__DEV__) {
-      ob.dep.notify({
-        type: TriggerOpTypes.ARRAY_MUTATION,
-        target: this,
-        key: method
-      })
-    } else {
-      ob.dep.notify()
-    }
-    return result
-  })
-})
diff --git a/src/core/observer/dep.ts b/src/core/observer/dep.ts
deleted file mode 100644
index 205efbbf5a0..00000000000
--- a/src/core/observer/dep.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-import config from '../config'
-import { DebuggerOptions, DebuggerEventExtraInfo } from 'v3'
-
-let uid = 0
-
-const pendingCleanupDeps: Dep[] = []
-
-export const cleanupDeps = () => {
-  for (let i = 0; i < pendingCleanupDeps.length; i++) {
-    const dep = pendingCleanupDeps[i]
-    dep.subs = dep.subs.filter(s => s)
-    dep._pending = false
-  }
-  pendingCleanupDeps.length = 0
-}
-
-/**
- * @internal
- */
-export interface DepTarget extends DebuggerOptions {
-  id: number
-  addDep(dep: Dep): void
-  update(): void
-}
-
-/**
- * A dep is an observable that can have multiple
- * directives subscribing to it.
- * @internal
- */
-export default class Dep {
-  static target?: DepTarget | null
-  id: number
-  subs: Array<DepTarget | null>
-  // pending subs cleanup
-  _pending = false
-
-  constructor() {
-    this.id = uid++
-    this.subs = []
-  }
-
-  addSub(sub: DepTarget) {
-    this.subs.push(sub)
-  }
-
-  removeSub(sub: DepTarget) {
-    // #12696 deps with massive amount of subscribers are extremely slow to
-    // clean up in Chromium
-    // to workaround this, we unset the sub for now, and clear them on
-    // next scheduler flush.
-    this.subs[this.subs.indexOf(sub)] = null
-    if (!this._pending) {
-      this._pending = true
-      pendingCleanupDeps.push(this)
-    }
-  }
-
-  depend(info?: DebuggerEventExtraInfo) {
-    if (Dep.target) {
-      Dep.target.addDep(this)
-      if (__DEV__ && info && Dep.target.onTrack) {
-        Dep.target.onTrack({
-          effect: Dep.target,
-          ...info
-        })
-      }
-    }
-  }
-
-  notify(info?: DebuggerEventExtraInfo) {
-    // stabilize the subscriber list first
-    const subs = this.subs.filter(s => s) as DepTarget[]
-    if (__DEV__ && !config.async) {
-      // subs aren't sorted in scheduler if not running async
-      // we need to sort them now to make sure they fire in correct
-      // order
-      subs.sort((a, b) => a.id - b.id)
-    }
-    for (let i = 0, l = subs.length; i < l; i++) {
-      const sub = subs[i]
-      if (__DEV__ && info) {
-        sub.onTrigger &&
-          sub.onTrigger({
-            effect: subs[i],
-            ...info
-          })
-      }
-      sub.update()
-    }
-  }
-}
-
-// The current target watcher being evaluated.
-// This is globally unique because only one watcher
-// can be evaluated at a time.
-Dep.target = null
-const targetStack: Array<DepTarget | null | undefined> = []
-
-export function pushTarget(target?: DepTarget | null) {
-  targetStack.push(target)
-  Dep.target = target
-}
-
-export function popTarget() {
-  targetStack.pop()
-  Dep.target = targetStack[targetStack.length - 1]
-}
diff --git a/src/core/observer/index.ts b/src/core/observer/index.ts
deleted file mode 100644
index dc6efae4d1b..00000000000
--- a/src/core/observer/index.ts
+++ /dev/null
@@ -1,339 +0,0 @@
-import Dep from './dep'
-import VNode from '../vdom/vnode'
-import { arrayMethods } from './array'
-import {
-  def,
-  warn,
-  hasOwn,
-  isArray,
-  hasProto,
-  isPlainObject,
-  isPrimitive,
-  isUndef,
-  isValidArrayIndex,
-  isServerRendering,
-  hasChanged,
-  noop
-} from '../util/index'
-import { isReadonly, isRef, TrackOpTypes, TriggerOpTypes } from '../../v3'
-
-const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
-
-const NO_INITIAL_VALUE = {}
-
-/**
- * In some cases we may want to disable observation inside a component's
- * update computation.
- */
-export let shouldObserve: boolean = true
-
-export function toggleObserving(value: boolean) {
-  shouldObserve = value
-}
-
-// ssr mock dep
-const mockDep = {
-  notify: noop,
-  depend: noop,
-  addSub: noop,
-  removeSub: noop
-} as Dep
-
-/**
- * Observer class that is attached to each observed
- * object. Once attached, the observer converts the target
- * object's property keys into getter/setters that
- * collect dependencies and dispatch updates.
- */
-export class Observer {
-  dep: Dep
-  vmCount: number // number of vms that have this object as root $data
-
-  constructor(public value: any, public shallow = false, public mock = false) {
-    // this.value = value
-    this.dep = mock ? mockDep : new Dep()
-    this.vmCount = 0
-    def(value, '__ob__', this)
-    if (isArray(value)) {
-      if (!mock) {
-        if (hasProto) {
-          /* eslint-disable no-proto */
-          ;(value as any).__proto__ = arrayMethods
-          /* eslint-enable no-proto */
-        } else {
-          for (let i = 0, l = arrayKeys.length; i < l; i++) {
-            const key = arrayKeys[i]
-            def(value, key, arrayMethods[key])
-          }
-        }
-      }
-      if (!shallow) {
-        this.observeArray(value)
-      }
-    } else {
-      /**
-       * Walk through all properties and convert them into
-       * getter/setters. This method should only be called when
-       * value type is Object.
-       */
-      const keys = Object.keys(value)
-      for (let i = 0; i < keys.length; i++) {
-        const key = keys[i]
-        defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock)
-      }
-    }
-  }
-
-  /**
-   * Observe a list of Array items.
-   */
-  observeArray(value: any[]) {
-    for (let i = 0, l = value.length; i < l; i++) {
-      observe(value[i], false, this.mock)
-    }
-  }
-}
-
-// helpers
-
-/**
- * Attempt to create an observer instance for a value,
- * returns the new observer if successfully observed,
- * or the existing observer if the value already has one.
- */
-export function observe(
-  value: any,
-  shallow?: boolean,
-  ssrMockReactivity?: boolean
-): Observer | void {
-  if (value && hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
-    return value.__ob__
-  }
-  if (
-    shouldObserve &&
-    (ssrMockReactivity || !isServerRendering()) &&
-    (isArray(value) || isPlainObject(value)) &&
-    Object.isExtensible(value) &&
-    !value.__v_skip /* ReactiveFlags.SKIP */ &&
-    !isRef(value) &&
-    !(value instanceof VNode)
-  ) {
-    return new Observer(value, shallow, ssrMockReactivity)
-  }
-}
-
-/**
- * Define a reactive property on an Object.
- */
-export function defineReactive(
-  obj: object,
-  key: string,
-  val?: any,
-  customSetter?: Function | null,
-  shallow?: boolean,
-  mock?: boolean,
-  observeEvenIfShallow = false
-) {
-  const dep = new Dep()
-
-  const property = Object.getOwnPropertyDescriptor(obj, key)
-  if (property && property.configurable === false) {
-    return
-  }
-
-  // cater for pre-defined getter/setters
-  const getter = property && property.get
-  const setter = property && property.set
-  if (
-    (!getter || setter) &&
-    (val === NO_INITIAL_VALUE || arguments.length === 2)
-  ) {
-    val = obj[key]
-  }
-
-  let childOb = shallow ? val && val.__ob__ : observe(val, false, mock)
-  Object.defineProperty(obj, key, {
-    enumerable: true,
-    configurable: true,
-    get: function reactiveGetter() {
-      const value = getter ? getter.call(obj) : val
-      if (Dep.target) {
-        if (__DEV__) {
-          dep.depend({
-            target: obj,
-            type: TrackOpTypes.GET,
-            key
-          })
-        } else {
-          dep.depend()
-        }
-        if (childOb) {
-          childOb.dep.depend()
-          if (isArray(value)) {
-            dependArray(value)
-          }
-        }
-      }
-      return isRef(value) && !shallow ? value.value : value
-    },
-    set: function reactiveSetter(newVal) {
-      const value = getter ? getter.call(obj) : val
-      if (!hasChanged(value, newVal)) {
-        return
-      }
-      if (__DEV__ && customSetter) {
-        customSetter()
-      }
-      if (setter) {
-        setter.call(obj, newVal)
-      } else if (getter) {
-        // #7981: for accessor properties without setter
-        return
-      } else if (!shallow && isRef(value) && !isRef(newVal)) {
-        value.value = newVal
-        return
-      } else {
-        val = newVal
-      }
-      childOb = shallow ? newVal && newVal.__ob__ : observe(newVal, false, mock)
-      if (__DEV__) {
-        dep.notify({
-          type: TriggerOpTypes.SET,
-          target: obj,
-          key,
-          newValue: newVal,
-          oldValue: value
-        })
-      } else {
-        dep.notify()
-      }
-    }
-  })
-
-  return dep
-}
-
-/**
- * Set a property on an object. Adds the new property and
- * triggers change notification if the property doesn't
- * already exist.
- */
-export function set<T>(array: T[], key: number, value: T): T
-export function set<T>(object: object, key: string | number, value: T): T
-export function set(
-  target: any[] | Record<string, any>,
-  key: any,
-  val: any
-): any {
-  if (__DEV__ && (isUndef(target) || isPrimitive(target))) {
-    warn(
-      `Cannot set reactive property on undefined, null, or primitive value: ${target}`
-    )
-  }
-  if (isReadonly(target)) {
-    __DEV__ && warn(`Set operation on key "${key}" failed: target is readonly.`)
-    return
-  }
-  const ob = (target as any).__ob__
-  if (isArray(target) && isValidArrayIndex(key)) {
-    target.length = Math.max(target.length, key)
-    target.splice(key, 1, val)
-    // when mocking for SSR, array methods are not hijacked
-    if (ob && !ob.shallow && ob.mock) {
-      observe(val, false, true)
-    }
-    return val
-  }
-  if (key in target && !(key in Object.prototype)) {
-    target[key] = val
-    return val
-  }
-  if ((target as any)._isVue || (ob && ob.vmCount)) {
-    __DEV__ &&
-      warn(
-        'Avoid adding reactive properties to a Vue instance or its root $data ' +
-          'at runtime - declare it upfront in the data option.'
-      )
-    return val
-  }
-  if (!ob) {
-    target[key] = val
-    return val
-  }
-  defineReactive(ob.value, key, val, undefined, ob.shallow, ob.mock)
-  if (__DEV__) {
-    ob.dep.notify({
-      type: TriggerOpTypes.ADD,
-      target: target,
-      key,
-      newValue: val,
-      oldValue: undefined
-    })
-  } else {
-    ob.dep.notify()
-  }
-  return val
-}
-
-/**
- * Delete a property and trigger change if necessary.
- */
-export function del<T>(array: T[], key: number): void
-export function del(object: object, key: string | number): void
-export function del(target: any[] | object, key: any) {
-  if (__DEV__ && (isUndef(target) || isPrimitive(target))) {
-    warn(
-      `Cannot delete reactive property on undefined, null, or primitive value: ${target}`
-    )
-  }
-  if (isArray(target) && isValidArrayIndex(key)) {
-    target.splice(key, 1)
-    return
-  }
-  const ob = (target as any).__ob__
-  if ((target as any)._isVue || (ob && ob.vmCount)) {
-    __DEV__ &&
-      warn(
-        'Avoid deleting properties on a Vue instance or its root $data ' +
-          '- just set it to null.'
-      )
-    return
-  }
-  if (isReadonly(target)) {
-    __DEV__ &&
-      warn(`Delete operation on key "${key}" failed: target is readonly.`)
-    return
-  }
-  if (!hasOwn(target, key)) {
-    return
-  }
-  delete target[key]
-  if (!ob) {
-    return
-  }
-  if (__DEV__) {
-    ob.dep.notify({
-      type: TriggerOpTypes.DELETE,
-      target: target,
-      key
-    })
-  } else {
-    ob.dep.notify()
-  }
-}
-
-/**
- * Collect dependencies on array elements when the array is touched, since
- * we cannot intercept array element access like property getters.
- */
-function dependArray(value: Array<any>) {
-  for (let e, i = 0, l = value.length; i < l; i++) {
-    e = value[i]
-    if (e && e.__ob__) {
-      e.__ob__.dep.depend()
-    }
-    if (isArray(e)) {
-      dependArray(e)
-    }
-  }
-}
diff --git a/src/core/observer/scheduler.ts b/src/core/observer/scheduler.ts
deleted file mode 100644
index 8fc82b48cf4..00000000000
--- a/src/core/observer/scheduler.ts
+++ /dev/null
@@ -1,199 +0,0 @@
-import type Watcher from './watcher'
-import config from '../config'
-import Dep, { cleanupDeps } from './dep'
-import { callHook, activateChildComponent } from '../instance/lifecycle'
-
-import { warn, nextTick, devtools, inBrowser, isIE } from '../util/index'
-import type { Component } from 'types/component'
-
-export const MAX_UPDATE_COUNT = 100
-
-const queue: Array<Watcher> = []
-const activatedChildren: Array<Component> = []
-let has: { [key: number]: true | undefined | null } = {}
-let circular: { [key: number]: number } = {}
-let waiting = false
-let flushing = false
-let index = 0
-
-/**
- * Reset the scheduler's state.
- */
-function resetSchedulerState() {
-  index = queue.length = activatedChildren.length = 0
-  has = {}
-  if (__DEV__) {
-    circular = {}
-  }
-  waiting = flushing = false
-}
-
-// Async edge case #6566 requires saving the timestamp when event listeners are
-// attached. However, calling performance.now() has a perf overhead especially
-// if the page has thousands of event listeners. Instead, we take a timestamp
-// every time the scheduler flushes and use that for all event listeners
-// attached during that flush.
-export let currentFlushTimestamp = 0
-
-// Async edge case fix requires storing an event listener's attach timestamp.
-let getNow: () => number = Date.now
-
-// Determine what event timestamp the browser is using. Annoyingly, the
-// timestamp can either be hi-res (relative to page load) or low-res
-// (relative to UNIX epoch), so in order to compare time we have to use the
-// same timestamp type when saving the flush timestamp.
-// All IE versions use low-res event timestamps, and have problematic clock
-// implementations (#9632)
-if (inBrowser && !isIE) {
-  const performance = window.performance
-  if (
-    performance &&
-    typeof performance.now === 'function' &&
-    getNow() > document.createEvent('Event').timeStamp
-  ) {
-    // if the event timestamp, although evaluated AFTER the Date.now(), is
-    // smaller than it, it means the event is using a hi-res timestamp,
-    // and we need to use the hi-res version for event listener timestamps as
-    // well.
-    getNow = () => performance.now()
-  }
-}
-
-const sortCompareFn = (a: Watcher, b: Watcher): number => {
-  if (a.post) {
-    if (!b.post) return 1
-  } else if (b.post) {
-    return -1
-  }
-  return a.id - b.id
-}
-
-/**
- * Flush both queues and run the watchers.
- */
-function flushSchedulerQueue() {
-  currentFlushTimestamp = getNow()
-  flushing = true
-  let watcher, id
-
-  // Sort queue before flush.
-  // This ensures that:
-  // 1. Components are updated from parent to child. (because parent is always
-  //    created before the child)
-  // 2. A component's user watchers are run before its render watcher (because
-  //    user watchers are created before the render watcher)
-  // 3. If a component is destroyed during a parent component's watcher run,
-  //    its watchers can be skipped.
-  queue.sort(sortCompareFn)
-
-  // do not cache length because more watchers might be pushed
-  // as we run existing watchers
-  for (index = 0; index < queue.length; index++) {
-    watcher = queue[index]
-    if (watcher.before) {
-      watcher.before()
-    }
-    id = watcher.id
-    has[id] = null
-    watcher.run()
-    // in dev build, check and stop circular updates.
-    if (__DEV__ && has[id] != null) {
-      circular[id] = (circular[id] || 0) + 1
-      if (circular[id] > MAX_UPDATE_COUNT) {
-        warn(
-          'You may have an infinite update loop ' +
-            (watcher.user
-              ? `in watcher with expression "${watcher.expression}"`
-              : `in a component render function.`),
-          watcher.vm
-        )
-        break
-      }
-    }
-  }
-
-  // keep copies of post queues before resetting state
-  const activatedQueue = activatedChildren.slice()
-  const updatedQueue = queue.slice()
-
-  resetSchedulerState()
-
-  // call component updated and activated hooks
-  callActivatedHooks(activatedQueue)
-  callUpdatedHooks(updatedQueue)
-  cleanupDeps()
-
-  // devtool hook
-  /* istanbul ignore if */
-  if (devtools && config.devtools) {
-    devtools.emit('flush')
-  }
-}
-
-function callUpdatedHooks(queue: Watcher[]) {
-  let i = queue.length
-  while (i--) {
-    const watcher = queue[i]
-    const vm = watcher.vm
-    if (vm && vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
-      callHook(vm, 'updated')
-    }
-  }
-}
-
-/**
- * Queue a kept-alive component that was activated during patch.
- * The queue will be processed after the entire tree has been patched.
- */
-export function queueActivatedComponent(vm: Component) {
-  // setting _inactive to false here so that a render function can
-  // rely on checking whether it's in an inactive tree (e.g. router-view)
-  vm._inactive = false
-  activatedChildren.push(vm)
-}
-
-function callActivatedHooks(queue) {
-  for (let i = 0; i < queue.length; i++) {
-    queue[i]._inactive = true
-    activateChildComponent(queue[i], true /* true */)
-  }
-}
-
-/**
- * Push a watcher into the watcher queue.
- * Jobs with duplicate IDs will be skipped unless it's
- * pushed when the queue is being flushed.
- */
-export function queueWatcher(watcher: Watcher) {
-  const id = watcher.id
-  if (has[id] != null) {
-    return
-  }
-
-  if (watcher === Dep.target && watcher.noRecurse) {
-    return
-  }
-
-  has[id] = true
-  if (!flushing) {
-    queue.push(watcher)
-  } else {
-    // if already flushing, splice the watcher based on its id
-    // if already past its id, it will be run next immediately.
-    let i = queue.length - 1
-    while (i > index && queue[i].id > watcher.id) {
-      i--
-    }
-    queue.splice(i + 1, 0, watcher)
-  }
-  // queue the flush
-  if (!waiting) {
-    waiting = true
-
-    if (__DEV__ && !config.async) {
-      flushSchedulerQueue()
-      return
-    }
-    nextTick(flushSchedulerQueue)
-  }
-}
diff --git a/src/core/observer/traverse.ts b/src/core/observer/traverse.ts
deleted file mode 100644
index c681e0c9285..00000000000
--- a/src/core/observer/traverse.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { _Set as Set, isObject, isArray } from '../util/index'
-import type { SimpleSet } from '../util/index'
-import VNode from '../vdom/vnode'
-import { isRef } from '../../v3'
-
-const seenObjects = new Set()
-
-/**
- * Recursively traverse an object to evoke all converted
- * getters, so that every nested property inside the object
- * is collected as a "deep" dependency.
- */
-export function traverse(val: any) {
-  _traverse(val, seenObjects)
-  seenObjects.clear()
-  return val
-}
-
-function _traverse(val: any, seen: SimpleSet) {
-  let i, keys
-  const isA = isArray(val)
-  if (
-    (!isA && !isObject(val)) ||
-    val.__v_skip /* ReactiveFlags.SKIP */ ||
-    Object.isFrozen(val) ||
-    val instanceof VNode
-  ) {
-    return
-  }
-  if (val.__ob__) {
-    const depId = val.__ob__.dep.id
-    if (seen.has(depId)) {
-      return
-    }
-    seen.add(depId)
-  }
-  if (isA) {
-    i = val.length
-    while (i--) _traverse(val[i], seen)
-  } else if (isRef(val)) {
-    _traverse(val.value, seen)
-  } else {
-    keys = Object.keys(val)
-    i = keys.length
-    while (i--) _traverse(val[keys[i]], seen)
-  }
-}
diff --git a/src/core/observer/watcher.ts b/src/core/observer/watcher.ts
deleted file mode 100644
index b2989b53772..00000000000
--- a/src/core/observer/watcher.ts
+++ /dev/null
@@ -1,278 +0,0 @@
-import {
-  warn,
-  remove,
-  isObject,
-  parsePath,
-  _Set as Set,
-  handleError,
-  invokeWithErrorHandling,
-  noop,
-  isFunction
-} from '../util/index'
-
-import { traverse } from './traverse'
-import { queueWatcher } from './scheduler'
-import Dep, { pushTarget, popTarget, DepTarget } from './dep'
-import { DebuggerEvent, DebuggerOptions } from 'v3/debug'
-
-import type { SimpleSet } from '../util/index'
-import type { Component } from 'types/component'
-import { activeEffectScope, recordEffectScope } from 'v3/reactivity/effectScope'
-
-let uid = 0
-
-/**
- * @internal
- */
-export interface WatcherOptions extends DebuggerOptions {
-  deep?: boolean
-  user?: boolean
-  lazy?: boolean
-  sync?: boolean
-  before?: Function
-}
-
-/**
- * A watcher parses an expression, collects dependencies,
- * and fires callback when the expression value changes.
- * This is used for both the $watch() api and directives.
- * @internal
- */
-export default class Watcher implements DepTarget {
-  vm?: Component | null
-  expression: string
-  cb: Function
-  id: number
-  deep: boolean
-  user: boolean
-  lazy: boolean
-  sync: boolean
-  dirty: boolean
-  active: boolean
-  deps: Array<Dep>
-  newDeps: Array<Dep>
-  depIds: SimpleSet
-  newDepIds: SimpleSet
-  before?: Function
-  onStop?: Function
-  noRecurse?: boolean
-  getter: Function
-  value: any
-  post: boolean
-
-  // dev only
-  onTrack?: ((event: DebuggerEvent) => void) | undefined
-  onTrigger?: ((event: DebuggerEvent) => void) | undefined
-
-  constructor(
-    vm: Component | null,
-    expOrFn: string | (() => any),
-    cb: Function,
-    options?: WatcherOptions | null,
-    isRenderWatcher?: boolean
-  ) {
-    recordEffectScope(
-      this,
-      // if the active effect scope is manually created (not a component scope),
-      // prioritize it
-      activeEffectScope && !activeEffectScope._vm
-        ? activeEffectScope
-        : vm
-        ? vm._scope
-        : undefined
-    )
-    if ((this.vm = vm) && isRenderWatcher) {
-      vm._watcher = this
-    }
-    // options
-    if (options) {
-      this.deep = !!options.deep
-      this.user = !!options.user
-      this.lazy = !!options.lazy
-      this.sync = !!options.sync
-      this.before = options.before
-      if (__DEV__) {
-        this.onTrack = options.onTrack
-        this.onTrigger = options.onTrigger
-      }
-    } else {
-      this.deep = this.user = this.lazy = this.sync = false
-    }
-    this.cb = cb
-    this.id = ++uid // uid for batching
-    this.active = true
-    this.post = false
-    this.dirty = this.lazy // for lazy watchers
-    this.deps = []
-    this.newDeps = []
-    this.depIds = new Set()
-    this.newDepIds = new Set()
-    this.expression = __DEV__ ? expOrFn.toString() : ''
-    // parse expression for getter
-    if (isFunction(expOrFn)) {
-      this.getter = expOrFn
-    } else {
-      this.getter = parsePath(expOrFn)
-      if (!this.getter) {
-        this.getter = noop
-        __DEV__ &&
-          warn(
-            `Failed watching path: "${expOrFn}" ` +
-              'Watcher only accepts simple dot-delimited paths. ' +
-              'For full control, use a function instead.',
-            vm
-          )
-      }
-    }
-    this.value = this.lazy ? undefined : this.get()
-  }
-
-  /**
-   * Evaluate the getter, and re-collect dependencies.
-   */
-  get() {
-    pushTarget(this)
-    let value
-    const vm = this.vm
-    try {
-      value = this.getter.call(vm, vm)
-    } catch (e: any) {
-      if (this.user) {
-        handleError(e, vm, `getter for watcher "${this.expression}"`)
-      } else {
-        throw e
-      }
-    } finally {
-      // "touch" every property so they are all tracked as
-      // dependencies for deep watching
-      if (this.deep) {
-        traverse(value)
-      }
-      popTarget()
-      this.cleanupDeps()
-    }
-    return value
-  }
-
-  /**
-   * Add a dependency to this directive.
-   */
-  addDep(dep: Dep) {
-    const id = dep.id
-    if (!this.newDepIds.has(id)) {
-      this.newDepIds.add(id)
-      this.newDeps.push(dep)
-      if (!this.depIds.has(id)) {
-        dep.addSub(this)
-      }
-    }
-  }
-
-  /**
-   * Clean up for dependency collection.
-   */
-  cleanupDeps() {
-    let i = this.deps.length
-    while (i--) {
-      const dep = this.deps[i]
-      if (!this.newDepIds.has(dep.id)) {
-        dep.removeSub(this)
-      }
-    }
-    let tmp: any = this.depIds
-    this.depIds = this.newDepIds
-    this.newDepIds = tmp
-    this.newDepIds.clear()
-    tmp = this.deps
-    this.deps = this.newDeps
-    this.newDeps = tmp
-    this.newDeps.length = 0
-  }
-
-  /**
-   * Subscriber interface.
-   * Will be called when a dependency changes.
-   */
-  update() {
-    /* istanbul ignore else */
-    if (this.lazy) {
-      this.dirty = true
-    } else if (this.sync) {
-      this.run()
-    } else {
-      queueWatcher(this)
-    }
-  }
-
-  /**
-   * Scheduler job interface.
-   * Will be called by the scheduler.
-   */
-  run() {
-    if (this.active) {
-      const value = this.get()
-      if (
-        value !== this.value ||
-        // Deep watchers and watchers on Object/Arrays should fire even
-        // when the value is the same, because the value may
-        // have mutated.
-        isObject(value) ||
-        this.deep
-      ) {
-        // set new value
-        const oldValue = this.value
-        this.value = value
-        if (this.user) {
-          const info = `callback for watcher "${this.expression}"`
-          invokeWithErrorHandling(
-            this.cb,
-            this.vm,
-            [value, oldValue],
-            this.vm,
-            info
-          )
-        } else {
-          this.cb.call(this.vm, value, oldValue)
-        }
-      }
-    }
-  }
-
-  /**
-   * Evaluate the value of the watcher.
-   * This only gets called for lazy watchers.
-   */
-  evaluate() {
-    this.value = this.get()
-    this.dirty = false
-  }
-
-  /**
-   * Depend on all deps collected by this watcher.
-   */
-  depend() {
-    let i = this.deps.length
-    while (i--) {
-      this.deps[i].depend()
-    }
-  }
-
-  /**
-   * Remove self from all dependencies' subscriber list.
-   */
-  teardown() {
-    if (this.vm && !this.vm._isBeingDestroyed) {
-      remove(this.vm._scope.effects, this)
-    }
-    if (this.active) {
-      let i = this.deps.length
-      while (i--) {
-        this.deps[i].removeSub(this)
-      }
-      this.active = false
-      if (this.onStop) {
-        this.onStop()
-      }
-    }
-  }
-}
diff --git a/src/core/util/debug.ts b/src/core/util/debug.ts
deleted file mode 100644
index 891f0177c68..00000000000
--- a/src/core/util/debug.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import config from '../config'
-import { noop, isArray, isFunction } from 'shared/util'
-import type { Component } from 'types/component'
-import { currentInstance } from 'v3/currentInstance'
-import { getComponentName } from '../vdom/create-component'
-
-export let warn: (msg: string, vm?: Component | null) => void = noop
-export let tip = noop
-export let generateComponentTrace: (vm: Component) => string // work around flow check
-export let formatComponentName: (vm: Component, includeFile?: false) => string
-
-if (__DEV__) {
-  const hasConsole = typeof console !== 'undefined'
-  const classifyRE = /(?:^|[-_])(\w)/g
-  const classify = str =>
-    str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, '')
-
-  warn = (msg, vm = currentInstance) => {
-    const trace = vm ? generateComponentTrace(vm) : ''
-
-    if (config.warnHandler) {
-      config.warnHandler.call(null, msg, vm, trace)
-    } else if (hasConsole && !config.silent) {
-      console.error(`[Vue warn]: ${msg}${trace}`)
-    }
-  }
-
-  tip = (msg, vm) => {
-    if (hasConsole && !config.silent) {
-      console.warn(`[Vue tip]: ${msg}` + (vm ? generateComponentTrace(vm) : ''))
-    }
-  }
-
-  formatComponentName = (vm, includeFile) => {
-    if (vm.$root === vm) {
-      return '<Root>'
-    }
-    const options =
-      isFunction(vm) && (vm as any).cid != null
-        ? (vm as any).options
-        : vm._isVue
-        ? vm.$options || (vm.constructor as any).options
-        : vm
-    let name = getComponentName(options)
-    const file = options.__file
-    if (!name && file) {
-      const match = file.match(/([^/\\]+)\.vue$/)
-      name = match && match[1]
-    }
-
-    return (
-      (name ? `<${classify(name)}>` : `<Anonymous>`) +
-      (file && includeFile !== false ? ` at ${file}` : '')
-    )
-  }
-
-  const repeat = (str, n) => {
-    let res = ''
-    while (n) {
-      if (n % 2 === 1) res += str
-      if (n > 1) str += str
-      n >>= 1
-    }
-    return res
-  }
-
-  generateComponentTrace = (vm: Component | undefined) => {
-    if ((vm as any)._isVue && vm!.$parent) {
-      const tree: any[] = []
-      let currentRecursiveSequence = 0
-      while (vm) {
-        if (tree.length > 0) {
-          const last = tree[tree.length - 1]
-          if (last.constructor === vm.constructor) {
-            currentRecursiveSequence++
-            vm = vm.$parent!
-            continue
-          } else if (currentRecursiveSequence > 0) {
-            tree[tree.length - 1] = [last, currentRecursiveSequence]
-            currentRecursiveSequence = 0
-          }
-        }
-        tree.push(vm)
-        vm = vm.$parent!
-      }
-      return (
-        '\n\nfound in\n\n' +
-        tree
-          .map(
-            (vm, i) =>
-              `${i === 0 ? '---> ' : repeat(' ', 5 + i * 2)}${
-                isArray(vm)
-                  ? `${formatComponentName(vm[0])}... (${
-                      vm[1]
-                    } recursive calls)`
-                  : formatComponentName(vm)
-              }`
-          )
-          .join('\n')
-      )
-    } else {
-      return `\n\n(found in ${formatComponentName(vm!)})`
-    }
-  }
-}
diff --git a/src/core/util/env.ts b/src/core/util/env.ts
deleted file mode 100644
index cf64f50b7a2..00000000000
--- a/src/core/util/env.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-// can we use __proto__?
-export const hasProto = '__proto__' in {}
-
-// Browser environment sniffing
-export const inBrowser = typeof window !== 'undefined'
-export const UA = inBrowser && window.navigator.userAgent.toLowerCase()
-export const isIE = UA && /msie|trident/.test(UA)
-export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
-export const isEdge = UA && UA.indexOf('edge/') > 0
-export const isAndroid = UA && UA.indexOf('android') > 0
-export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA)
-export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge
-export const isPhantomJS = UA && /phantomjs/.test(UA)
-export const isFF = UA && UA.match(/firefox\/(\d+)/)
-
-// Firefox has a "watch" function on Object.prototype...
-// @ts-expect-error firebox support
-export const nativeWatch = {}.watch
-
-export let supportsPassive = false
-if (inBrowser) {
-  try {
-    const opts = {}
-    Object.defineProperty(opts, 'passive', {
-      get() {
-        /* istanbul ignore next */
-        supportsPassive = true
-      }
-    } as object) // https://github.com/facebook/flow/issues/285
-    window.addEventListener('test-passive', null as any, opts)
-  } catch (e: any) {}
-}
-
-// this needs to be lazy-evaled because vue may be required before
-// vue-server-renderer can set VUE_ENV
-let _isServer
-export const isServerRendering = () => {
-  if (_isServer === undefined) {
-    /* istanbul ignore if */
-    if (!inBrowser && typeof global !== 'undefined') {
-      // detect presence of vue-server-renderer and avoid
-      // Webpack shimming the process
-      _isServer =
-        global['process'] && global['process'].env.VUE_ENV === 'server'
-    } else {
-      _isServer = false
-    }
-  }
-  return _isServer
-}
-
-// detect devtools
-export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__
-
-/* istanbul ignore next */
-export function isNative(Ctor: any): boolean {
-  return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
-}
-
-export const hasSymbol =
-  typeof Symbol !== 'undefined' &&
-  isNative(Symbol) &&
-  typeof Reflect !== 'undefined' &&
-  isNative(Reflect.ownKeys)
-
-let _Set // $flow-disable-line
-/* istanbul ignore if */ if (typeof Set !== 'undefined' && isNative(Set)) {
-  // use native Set when available.
-  _Set = Set
-} else {
-  // a non-standard Set polyfill that only works with primitive keys.
-  _Set = class Set implements SimpleSet {
-    set: Record<string, boolean> = Object.create(null)
-
-    has(key: string | number) {
-      return this.set[key] === true
-    }
-    add(key: string | number) {
-      this.set[key] = true
-    }
-    clear() {
-      this.set = Object.create(null)
-    }
-  }
-}
-
-export interface SimpleSet {
-  has(key: string | number): boolean
-  add(key: string | number): any
-  clear(): void
-}
-
-export { _Set }
diff --git a/src/core/util/error.ts b/src/core/util/error.ts
deleted file mode 100644
index d97f847fd21..00000000000
--- a/src/core/util/error.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import config from '../config'
-import { warn } from './debug'
-import { inBrowser } from './env'
-import { isPromise } from 'shared/util'
-import { pushTarget, popTarget } from '../observer/dep'
-
-export function handleError(err: Error, vm: any, info: string) {
-  // Deactivate deps tracking while processing error handler to avoid possible infinite rendering.
-  // See: https://github.com/vuejs/vuex/issues/1505
-  pushTarget()
-  try {
-    if (vm) {
-      let cur = vm
-      while ((cur = cur.$parent)) {
-        const hooks = cur.$options.errorCaptured
-        if (hooks) {
-          for (let i = 0; i < hooks.length; i++) {
-            try {
-              const capture = hooks[i].call(cur, err, vm, info) === false
-              if (capture) return
-            } catch (e: any) {
-              globalHandleError(e, cur, 'errorCaptured hook')
-            }
-          }
-        }
-      }
-    }
-    globalHandleError(err, vm, info)
-  } finally {
-    popTarget()
-  }
-}
-
-export function invokeWithErrorHandling(
-  handler: Function,
-  context: any,
-  args: null | any[],
-  vm: any,
-  info: string
-) {
-  let res
-  try {
-    res = args ? handler.apply(context, args) : handler.call(context)
-    if (res && !res._isVue && isPromise(res) && !(res as any)._handled) {
-      res.catch(e => handleError(e, vm, info + ` (Promise/async)`))
-      // issue #9511
-      // avoid catch triggering multiple times when nested calls
-      ;(res as any)._handled = true
-    }
-  } catch (e: any) {
-    handleError(e, vm, info)
-  }
-  return res
-}
-
-function globalHandleError(err, vm, info) {
-  if (config.errorHandler) {
-    try {
-      return config.errorHandler.call(null, err, vm, info)
-    } catch (e: any) {
-      // if the user intentionally throws the original error in the handler,
-      // do not log it twice
-      if (e !== err) {
-        logError(e, null, 'config.errorHandler')
-      }
-    }
-  }
-  logError(err, vm, info)
-}
-
-function logError(err, vm, info) {
-  if (__DEV__) {
-    warn(`Error in ${info}: "${err.toString()}"`, vm)
-  }
-  /* istanbul ignore else */
-  if (inBrowser && typeof console !== 'undefined') {
-    console.error(err)
-  } else {
-    throw err
-  }
-}
diff --git a/src/core/util/lang.ts b/src/core/util/lang.ts
deleted file mode 100644
index 1d332f66027..00000000000
--- a/src/core/util/lang.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * unicode letters used for parsing html tags, component names and property paths.
- * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname
- * skipping \u10000-\uEFFFF due to it freezing up PhantomJS
- */
-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/
-
-/**
- * Check if a string starts with $ or _
- */
-export function isReserved(str: string): boolean {
-  const c = (str + '').charCodeAt(0)
-  return c === 0x24 || c === 0x5f
-}
-
-/**
- * Define a property.
- */
-export function def(obj: Object, key: string, val: any, enumerable?: boolean) {
-  Object.defineProperty(obj, key, {
-    value: val,
-    enumerable: !!enumerable,
-    writable: true,
-    configurable: true
-  })
-}
-
-/**
- * Parse simple path.
- */
-const bailRE = new RegExp(`[^${unicodeRegExp.source}.$_\\d]`)
-export function parsePath(path: string): any {
-  if (bailRE.test(path)) {
-    return
-  }
-  const segments = path.split('.')
-  return function (obj) {
-    for (let i = 0; i < segments.length; i++) {
-      if (!obj) return
-      obj = obj[segments[i]]
-    }
-    return obj
-  }
-}
diff --git a/src/core/util/next-tick.ts b/src/core/util/next-tick.ts
deleted file mode 100644
index 5e67bd7d2e7..00000000000
--- a/src/core/util/next-tick.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-/* globals MutationObserver */
-
-import { noop } from 'shared/util'
-import { handleError } from './error'
-import { isIE, isIOS, isNative } from './env'
-
-export let isUsingMicroTask = false
-
-const callbacks: Array<Function> = []
-let pending = false
-
-function flushCallbacks() {
-  pending = false
-  const copies = callbacks.slice(0)
-  callbacks.length = 0
-  for (let i = 0; i < copies.length; i++) {
-    copies[i]()
-  }
-}
-
-// Here we have async deferring wrappers using microtasks.
-// In 2.5 we used (macro) tasks (in combination with microtasks).
-// However, it has subtle problems when state is changed right before repaint
-// (e.g. #6813, out-in transitions).
-// Also, using (macro) tasks in event handler would cause some weird behaviors
-// that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109).
-// So we now use microtasks everywhere, again.
-// A major drawback of this tradeoff is that there are some scenarios
-// where microtasks have too high a priority and fire in between supposedly
-// sequential events (e.g. #4521, #6690, which have workarounds)
-// or even between bubbling of the same event (#6566).
-let timerFunc
-
-// The nextTick behavior leverages the microtask queue, which can be accessed
-// via either native Promise.then or MutationObserver.
-// MutationObserver has wider support, however it is seriously bugged in
-// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
-// completely stops working after triggering a few times... so, if native
-// Promise is available, we will use it:
-/* istanbul ignore next, $flow-disable-line */
-if (typeof Promise !== 'undefined' && isNative(Promise)) {
-  const p = Promise.resolve()
-  timerFunc = () => {
-    p.then(flushCallbacks)
-    // In problematic UIWebViews, Promise.then doesn't completely break, but
-    // it can get stuck in a weird state where callbacks are pushed into the
-    // microtask queue but the queue isn't being flushed, until the browser
-    // needs to do some other work, e.g. handle a timer. Therefore we can
-    // "force" the microtask queue to be flushed by adding an empty timer.
-    if (isIOS) setTimeout(noop)
-  }
-  isUsingMicroTask = true
-} else if (
-  !isIE &&
-  typeof MutationObserver !== 'undefined' &&
-  (isNative(MutationObserver) ||
-    // PhantomJS and iOS 7.x
-    MutationObserver.toString() === '[object MutationObserverConstructor]')
-) {
-  // Use MutationObserver where native Promise is not available,
-  // e.g. PhantomJS, iOS7, Android 4.4
-  // (#6466 MutationObserver is unreliable in IE11)
-  let counter = 1
-  const observer = new MutationObserver(flushCallbacks)
-  const textNode = document.createTextNode(String(counter))
-  observer.observe(textNode, {
-    characterData: true
-  })
-  timerFunc = () => {
-    counter = (counter + 1) % 2
-    textNode.data = String(counter)
-  }
-  isUsingMicroTask = true
-} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
-  // Fallback to setImmediate.
-  // Technically it leverages the (macro) task queue,
-  // but it is still a better choice than setTimeout.
-  timerFunc = () => {
-    setImmediate(flushCallbacks)
-  }
-} else {
-  // Fallback to setTimeout.
-  timerFunc = () => {
-    setTimeout(flushCallbacks, 0)
-  }
-}
-
-export function nextTick(): Promise<void>
-export function nextTick<T>(this: T, cb: (this: T, ...args: any[]) => any): void
-export function nextTick<T>(cb: (this: T, ...args: any[]) => any, ctx: T): void
-/**
- * @internal
- */
-export function nextTick(cb?: (...args: any[]) => any, ctx?: object) {
-  let _resolve
-  callbacks.push(() => {
-    if (cb) {
-      try {
-        cb.call(ctx)
-      } catch (e: any) {
-        handleError(e, ctx, 'nextTick')
-      }
-    } else if (_resolve) {
-      _resolve(ctx)
-    }
-  })
-  if (!pending) {
-    pending = true
-    timerFunc()
-  }
-  // $flow-disable-line
-  if (!cb && typeof Promise !== 'undefined') {
-    return new Promise(resolve => {
-      _resolve = resolve
-    })
-  }
-}
diff --git a/src/core/util/options.ts b/src/core/util/options.ts
deleted file mode 100644
index ef6a10342a9..00000000000
--- a/src/core/util/options.ts
+++ /dev/null
@@ -1,489 +0,0 @@
-import config from '../config'
-import { warn } from './debug'
-import { set } from '../observer/index'
-import { unicodeRegExp } from './lang'
-import { nativeWatch, hasSymbol } from './env'
-import { isArray, isFunction } from 'shared/util'
-
-import { ASSET_TYPES, LIFECYCLE_HOOKS } from 'shared/constants'
-
-import {
-  extend,
-  hasOwn,
-  camelize,
-  toRawType,
-  capitalize,
-  isBuiltInTag,
-  isPlainObject
-} from 'shared/util'
-import type { Component } from 'types/component'
-import type { ComponentOptions } from 'types/options'
-
-/**
- * Option overwriting strategies are functions that handle
- * how to merge a parent option value and a child option
- * value into the final value.
- */
-const strats = config.optionMergeStrategies
-
-/**
- * Options with restrictions
- */
-if (__DEV__) {
-  strats.el = strats.propsData = function (
-    parent: any,
-    child: any,
-    vm: any,
-    key: any
-  ) {
-    if (!vm) {
-      warn(
-        `option "${key}" can only be used during instance ` +
-          'creation with the `new` keyword.'
-      )
-    }
-    return defaultStrat(parent, child)
-  }
-}
-
-/**
- * Helper that recursively merges two data objects together.
- */
-function mergeData(
-  to: Record<string | symbol, any>,
-  from: Record<string | symbol, any> | null,
-  recursive = true
-): Record<PropertyKey, any> {
-  if (!from) return to
-  let key, toVal, fromVal
-
-  const keys = hasSymbol
-    ? (Reflect.ownKeys(from) as string[])
-    : Object.keys(from)
-
-  for (let i = 0; i < keys.length; i++) {
-    key = keys[i]
-    // in case the object is already observed...
-    if (key === '__ob__') continue
-    toVal = to[key]
-    fromVal = from[key]
-    if (!recursive || !hasOwn(to, key)) {
-      set(to, key, fromVal)
-    } else if (
-      toVal !== fromVal &&
-      isPlainObject(toVal) &&
-      isPlainObject(fromVal)
-    ) {
-      mergeData(toVal, fromVal)
-    }
-  }
-  return to
-}
-
-/**
- * Data
- */
-export function mergeDataOrFn(
-  parentVal: any,
-  childVal: any,
-  vm?: Component
-): Function | null {
-  if (!vm) {
-    // in a Vue.extend merge, both should be functions
-    if (!childVal) {
-      return parentVal
-    }
-    if (!parentVal) {
-      return childVal
-    }
-    // when parentVal & childVal are both present,
-    // we need to return a function that returns the
-    // merged result of both functions... no need to
-    // check if parentVal is a function here because
-    // it has to be a function to pass previous merges.
-    return function mergedDataFn() {
-      return mergeData(
-        isFunction(childVal) ? childVal.call(this, this) : childVal,
-        isFunction(parentVal) ? parentVal.call(this, this) : parentVal
-      )
-    }
-  } else {
-    return function mergedInstanceDataFn() {
-      // instance merge
-      const instanceData = isFunction(childVal)
-        ? childVal.call(vm, vm)
-        : childVal
-      const defaultData = isFunction(parentVal)
-        ? parentVal.call(vm, vm)
-        : parentVal
-      if (instanceData) {
-        return mergeData(instanceData, defaultData)
-      } else {
-        return defaultData
-      }
-    }
-  }
-}
-
-strats.data = function (
-  parentVal: any,
-  childVal: any,
-  vm?: Component
-): Function | null {
-  if (!vm) {
-    if (childVal && typeof childVal !== 'function') {
-      __DEV__ &&
-        warn(
-          'The "data" option should be a function ' +
-            'that returns a per-instance value in component ' +
-            'definitions.',
-          vm
-        )
-
-      return parentVal
-    }
-    return mergeDataOrFn(parentVal, childVal)
-  }
-
-  return mergeDataOrFn(parentVal, childVal, vm)
-}
-
-/**
- * Hooks and props are merged as arrays.
- */
-export function mergeLifecycleHook(
-  parentVal: Array<Function> | null,
-  childVal: Function | Array<Function> | null
-): Array<Function> | null {
-  const res = childVal
-    ? parentVal
-      ? parentVal.concat(childVal)
-      : isArray(childVal)
-      ? childVal
-      : [childVal]
-    : parentVal
-  return res ? dedupeHooks(res) : res
-}
-
-function dedupeHooks(hooks: any) {
-  const res: Array<any> = []
-  for (let i = 0; i < hooks.length; i++) {
-    if (res.indexOf(hooks[i]) === -1) {
-      res.push(hooks[i])
-    }
-  }
-  return res
-}
-
-LIFECYCLE_HOOKS.forEach(hook => {
-  strats[hook] = mergeLifecycleHook
-})
-
-/**
- * Assets
- *
- * When a vm is present (instance creation), we need to do
- * a three-way merge between constructor options, instance
- * options and parent options.
- */
-function mergeAssets(
-  parentVal: Object | null,
-  childVal: Object | null,
-  vm: Component | null,
-  key: string
-): Object {
-  const res = Object.create(parentVal || null)
-  if (childVal) {
-    __DEV__ && assertObjectType(key, childVal, vm)
-    return extend(res, childVal)
-  } else {
-    return res
-  }
-}
-
-ASSET_TYPES.forEach(function (type) {
-  strats[type + 's'] = mergeAssets
-})
-
-/**
- * Watchers.
- *
- * Watchers hashes should not overwrite one
- * another, so we merge them as arrays.
- */
-strats.watch = function (
-  parentVal: Record<string, any> | null,
-  childVal: Record<string, any> | null,
-  vm: Component | null,
-  key: string
-): Object | null {
-  // work around Firefox's Object.prototype.watch...
-  //@ts-expect-error work around
-  if (parentVal === nativeWatch) parentVal = undefined
-  //@ts-expect-error work around
-  if (childVal === nativeWatch) childVal = undefined
-  /* istanbul ignore if */
-  if (!childVal) return Object.create(parentVal || null)
-  if (__DEV__) {
-    assertObjectType(key, childVal, vm)
-  }
-  if (!parentVal) return childVal
-  const ret: Record<string, any> = {}
-  extend(ret, parentVal)
-  for (const key in childVal) {
-    let parent = ret[key]
-    const child = childVal[key]
-    if (parent && !isArray(parent)) {
-      parent = [parent]
-    }
-    ret[key] = parent ? parent.concat(child) : isArray(child) ? child : [child]
-  }
-  return ret
-}
-
-/**
- * Other object hashes.
- */
-strats.props =
-  strats.methods =
-  strats.inject =
-  strats.computed =
-    function (
-      parentVal: Object | null,
-      childVal: Object | null,
-      vm: Component | null,
-      key: string
-    ): Object | null {
-      if (childVal && __DEV__) {
-        assertObjectType(key, childVal, vm)
-      }
-      if (!parentVal) return childVal
-      const ret = Object.create(null)
-      extend(ret, parentVal)
-      if (childVal) extend(ret, childVal)
-      return ret
-    }
-
-strats.provide = function (parentVal: Object | null, childVal: Object | null) {
-  if (!parentVal) return childVal
-  return function () {
-    const ret = Object.create(null)
-    mergeData(ret, isFunction(parentVal) ? parentVal.call(this) : parentVal)
-    if (childVal) {
-      mergeData(
-        ret,
-        isFunction(childVal) ? childVal.call(this) : childVal,
-        false // non-recursive
-      )
-    }
-    return ret
-  }
-}
-
-/**
- * Default strategy.
- */
-const defaultStrat = function (parentVal: any, childVal: any): any {
-  return childVal === undefined ? parentVal : childVal
-}
-
-/**
- * Validate component names
- */
-function checkComponents(options: Record<string, any>) {
-  for (const key in options.components) {
-    validateComponentName(key)
-  }
-}
-
-export function validateComponentName(name: string) {
-  if (
-    !new RegExp(`^[a-zA-Z][\\-\\.0-9_${unicodeRegExp.source}]*$`).test(name)
-  ) {
-    warn(
-      'Invalid component name: "' +
-        name +
-        '". Component names ' +
-        'should conform to valid custom element name in html5 specification.'
-    )
-  }
-  if (isBuiltInTag(name) || config.isReservedTag(name)) {
-    warn(
-      'Do not use built-in or reserved HTML elements as component ' +
-        'id: ' +
-        name
-    )
-  }
-}
-
-/**
- * Ensure all props option syntax are normalized into the
- * Object-based format.
- */
-function normalizeProps(options: Record<string, any>, vm?: Component | null) {
-  const props = options.props
-  if (!props) return
-  const res: Record<string, any> = {}
-  let i, val, name
-  if (isArray(props)) {
-    i = props.length
-    while (i--) {
-      val = props[i]
-      if (typeof val === 'string') {
-        name = camelize(val)
-        res[name] = { type: null }
-      } else if (__DEV__) {
-        warn('props must be strings when using array syntax.')
-      }
-    }
-  } else if (isPlainObject(props)) {
-    for (const key in props) {
-      val = props[key]
-      name = camelize(key)
-      res[name] = isPlainObject(val) ? val : { type: val }
-    }
-  } else if (__DEV__) {
-    warn(
-      `Invalid value for option "props": expected an Array or an Object, ` +
-        `but got ${toRawType(props)}.`,
-      vm
-    )
-  }
-  options.props = res
-}
-
-/**
- * Normalize all injections into Object-based format
- */
-function normalizeInject(options: Record<string, any>, vm?: Component | null) {
-  const inject = options.inject
-  if (!inject) return
-  const normalized: Record<string, any> = (options.inject = {})
-  if (isArray(inject)) {
-    for (let i = 0; i < inject.length; i++) {
-      normalized[inject[i]] = { from: inject[i] }
-    }
-  } else if (isPlainObject(inject)) {
-    for (const key in inject) {
-      const val = inject[key]
-      normalized[key] = isPlainObject(val)
-        ? extend({ from: key }, val)
-        : { from: val }
-    }
-  } else if (__DEV__) {
-    warn(
-      `Invalid value for option "inject": expected an Array or an Object, ` +
-        `but got ${toRawType(inject)}.`,
-      vm
-    )
-  }
-}
-
-/**
- * Normalize raw function directives into object format.
- */
-function normalizeDirectives(options: Record<string, any>) {
-  const dirs = options.directives
-  if (dirs) {
-    for (const key in dirs) {
-      const def = dirs[key]
-      if (isFunction(def)) {
-        dirs[key] = { bind: def, update: def }
-      }
-    }
-  }
-}
-
-function assertObjectType(name: string, value: any, vm: Component | null) {
-  if (!isPlainObject(value)) {
-    warn(
-      `Invalid value for option "${name}": expected an Object, ` +
-        `but got ${toRawType(value)}.`,
-      vm
-    )
-  }
-}
-
-/**
- * Merge two option objects into a new one.
- * Core utility used in both instantiation and inheritance.
- */
-export function mergeOptions(
-  parent: Record<string, any>,
-  child: Record<string, any>,
-  vm?: Component | null
-): ComponentOptions {
-  if (__DEV__) {
-    checkComponents(child)
-  }
-
-  if (isFunction(child)) {
-    // @ts-expect-error
-    child = child.options
-  }
-
-  normalizeProps(child, vm)
-  normalizeInject(child, vm)
-  normalizeDirectives(child)
-
-  // Apply extends and mixins on the child options,
-  // but only if it is a raw options object that isn't
-  // the result of another mergeOptions call.
-  // Only merged options has the _base property.
-  if (!child._base) {
-    if (child.extends) {
-      parent = mergeOptions(parent, child.extends, vm)
-    }
-    if (child.mixins) {
-      for (let i = 0, l = child.mixins.length; i < l; i++) {
-        parent = mergeOptions(parent, child.mixins[i], vm)
-      }
-    }
-  }
-
-  const options: ComponentOptions = {} as any
-  let key
-  for (key in parent) {
-    mergeField(key)
-  }
-  for (key in child) {
-    if (!hasOwn(parent, key)) {
-      mergeField(key)
-    }
-  }
-  function mergeField(key: any) {
-    const strat = strats[key] || defaultStrat
-    options[key] = strat(parent[key], child[key], vm, key)
-  }
-  return options
-}
-
-/**
- * Resolve an asset.
- * This function is used because child instances need access
- * to assets defined in its ancestor chain.
- */
-export function resolveAsset(
-  options: Record<string, any>,
-  type: string,
-  id: string,
-  warnMissing?: boolean
-): any {
-  /* istanbul ignore if */
-  if (typeof id !== 'string') {
-    return
-  }
-  const assets = options[type]
-  // check local registration variations first
-  if (hasOwn(assets, id)) return assets[id]
-  const camelizedId = camelize(id)
-  if (hasOwn(assets, camelizedId)) return assets[camelizedId]
-  const PascalCaseId = capitalize(camelizedId)
-  if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId]
-  // fallback to prototype chain
-  const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]
-  if (__DEV__ && warnMissing && !res) {
-    warn('Failed to resolve ' + type.slice(0, -1) + ': ' + id)
-  }
-  return res
-}
diff --git a/src/core/util/perf.ts b/src/core/util/perf.ts
deleted file mode 100644
index 6b6f0a00a3d..00000000000
--- a/src/core/util/perf.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { inBrowser } from './env'
-
-export let mark
-export let measure
-
-if (__DEV__) {
-  const perf = inBrowser && window.performance
-  /* istanbul ignore if */
-  if (
-    perf &&
-    // @ts-ignore
-    perf.mark &&
-    // @ts-ignore
-    perf.measure &&
-    // @ts-ignore
-    perf.clearMarks &&
-    // @ts-ignore
-    perf.clearMeasures
-  ) {
-    mark = tag => perf.mark(tag)
-    measure = (name, startTag, endTag) => {
-      perf.measure(name, startTag, endTag)
-      perf.clearMarks(startTag)
-      perf.clearMarks(endTag)
-      // perf.clearMeasures(name)
-    }
-  }
-}
diff --git a/src/core/util/props.ts b/src/core/util/props.ts
deleted file mode 100644
index 795f5ac37a6..00000000000
--- a/src/core/util/props.ts
+++ /dev/null
@@ -1,254 +0,0 @@
-import { warn } from './debug'
-import { observe, toggleObserving, shouldObserve } from '../observer/index'
-import {
-  hasOwn,
-  isArray,
-  isObject,
-  isFunction,
-  toRawType,
-  hyphenate,
-  capitalize,
-  isPlainObject
-} from 'shared/util'
-import type { Component } from 'types/component'
-
-type PropOptions = {
-  type: Function | Array<Function> | null
-  default: any
-  required?: boolean
-  validator?: Function
-}
-
-export function validateProp(
-  key: string,
-  propOptions: Object,
-  propsData: Object,
-  vm?: Component
-): any {
-  const prop = propOptions[key]
-  const absent = !hasOwn(propsData, key)
-  let value = propsData[key]
-  // boolean casting
-  const booleanIndex = getTypeIndex(Boolean, prop.type)
-  if (booleanIndex > -1) {
-    if (absent && !hasOwn(prop, 'default')) {
-      value = false
-    } else if (value === '' || value === hyphenate(key)) {
-      // only cast empty string / same name to boolean if
-      // boolean has higher priority
-      const stringIndex = getTypeIndex(String, prop.type)
-      if (stringIndex < 0 || booleanIndex < stringIndex) {
-        value = true
-      }
-    }
-  }
-  // check default value
-  if (value === undefined) {
-    value = getPropDefaultValue(vm, prop, key)
-    // since the default value is a fresh copy,
-    // make sure to observe it.
-    const prevShouldObserve = shouldObserve
-    toggleObserving(true)
-    observe(value)
-    toggleObserving(prevShouldObserve)
-  }
-  if (__DEV__) {
-    assertProp(prop, key, value, vm, absent)
-  }
-  return value
-}
-
-/**
- * Get the default value of a prop.
- */
-function getPropDefaultValue(
-  vm: Component | undefined,
-  prop: PropOptions,
-  key: string
-): any {
-  // no default, return undefined
-  if (!hasOwn(prop, 'default')) {
-    return undefined
-  }
-  const def = prop.default
-  // warn against non-factory defaults for Object & Array
-  if (__DEV__ && isObject(def)) {
-    warn(
-      'Invalid default value for prop "' +
-        key +
-        '": ' +
-        'Props with type Object/Array must use a factory function ' +
-        'to return the default value.',
-      vm
-    )
-  }
-  // the raw prop value was also undefined from previous render,
-  // return previous default value to avoid unnecessary watcher trigger
-  if (
-    vm &&
-    vm.$options.propsData &&
-    vm.$options.propsData[key] === undefined &&
-    vm._props[key] !== undefined
-  ) {
-    return vm._props[key]
-  }
-  // call factory function for non-Function types
-  // a value is Function if its prototype is function even across different execution context
-  return isFunction(def) && getType(prop.type) !== 'Function'
-    ? def.call(vm)
-    : def
-}
-
-/**
- * Assert whether a prop is valid.
- */
-function assertProp(
-  prop: PropOptions,
-  name: string,
-  value: any,
-  vm?: Component,
-  absent?: boolean
-) {
-  if (prop.required && absent) {
-    warn('Missing required prop: "' + name + '"', vm)
-    return
-  }
-  if (value == null && !prop.required) {
-    return
-  }
-  let type = prop.type
-  let valid = !type || (type as any) === true
-  const expectedTypes: string[] = []
-  if (type) {
-    if (!isArray(type)) {
-      type = [type]
-    }
-    for (let i = 0; i < type.length && !valid; i++) {
-      const assertedType = assertType(value, type[i], vm)
-      expectedTypes.push(assertedType.expectedType || '')
-      valid = assertedType.valid
-    }
-  }
-
-  const haveExpectedTypes = expectedTypes.some(t => t)
-  if (!valid && haveExpectedTypes) {
-    warn(getInvalidTypeMessage(name, value, expectedTypes), vm)
-    return
-  }
-  const validator = prop.validator
-  if (validator) {
-    if (!validator(value)) {
-      warn(
-        'Invalid prop: custom validator check failed for prop "' + name + '".',
-        vm
-      )
-    }
-  }
-}
-
-const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol|BigInt)$/
-
-function assertType(
-  value: any,
-  type: Function,
-  vm?: Component
-): {
-  valid: boolean
-  expectedType: string
-} {
-  let valid
-  const expectedType = getType(type)
-  if (simpleCheckRE.test(expectedType)) {
-    const t = typeof value
-    valid = t === expectedType.toLowerCase()
-    // for primitive wrapper objects
-    if (!valid && t === 'object') {
-      valid = value instanceof type
-    }
-  } else if (expectedType === 'Object') {
-    valid = isPlainObject(value)
-  } else if (expectedType === 'Array') {
-    valid = isArray(value)
-  } else {
-    try {
-      valid = value instanceof type
-    } catch (e: any) {
-      warn('Invalid prop type: "' + String(type) + '" is not a constructor', vm)
-      valid = false
-    }
-  }
-  return {
-    valid,
-    expectedType
-  }
-}
-
-const functionTypeCheckRE = /^\s*function (\w+)/
-
-/**
- * Use function string name to check built-in types,
- * because a simple equality check will fail when running
- * across different vms / iframes.
- */
-function getType(fn) {
-  const match = fn && fn.toString().match(functionTypeCheckRE)
-  return match ? match[1] : ''
-}
-
-function isSameType(a, b) {
-  return getType(a) === getType(b)
-}
-
-function getTypeIndex(type, expectedTypes): number {
-  if (!isArray(expectedTypes)) {
-    return isSameType(expectedTypes, type) ? 0 : -1
-  }
-  for (let i = 0, len = expectedTypes.length; i < len; i++) {
-    if (isSameType(expectedTypes[i], type)) {
-      return i
-    }
-  }
-  return -1
-}
-
-function getInvalidTypeMessage(name, value, expectedTypes) {
-  let message =
-    `Invalid prop: type check failed for prop "${name}".` +
-    ` Expected ${expectedTypes.map(capitalize).join(', ')}`
-  const expectedType = expectedTypes[0]
-  const receivedType = toRawType(value)
-  // check if we need to specify expected value
-  if (
-    expectedTypes.length === 1 &&
-    isExplicable(expectedType) &&
-    isExplicable(typeof value) &&
-    !isBoolean(expectedType, receivedType)
-  ) {
-    message += ` with value ${styleValue(value, expectedType)}`
-  }
-  message += `, got ${receivedType} `
-  // check if we need to specify received value
-  if (isExplicable(receivedType)) {
-    message += `with value ${styleValue(value, receivedType)}.`
-  }
-  return message
-}
-
-function styleValue(value, type) {
-  if (type === 'String') {
-    return `"${value}"`
-  } else if (type === 'Number') {
-    return `${Number(value)}`
-  } else {
-    return `${value}`
-  }
-}
-
-const EXPLICABLE_TYPES = ['string', 'number', 'boolean']
-function isExplicable(value) {
-  return EXPLICABLE_TYPES.some(elem => value.toLowerCase() === elem)
-}
-
-function isBoolean(...args) {
-  return args.some(elem => elem.toLowerCase() === 'boolean')
-}
diff --git a/src/core/vdom/create-component.ts b/src/core/vdom/create-component.ts
deleted file mode 100644
index 9e48c575230..00000000000
--- a/src/core/vdom/create-component.ts
+++ /dev/null
@@ -1,275 +0,0 @@
-import VNode from './vnode'
-import { isArray } from 'core/util'
-import { resolveConstructorOptions } from 'core/instance/init'
-import { queueActivatedComponent } from 'core/observer/scheduler'
-import { createFunctionalComponent } from './create-functional-component'
-
-import { warn, isDef, isUndef, isTrue, isObject } from '../util/index'
-
-import {
-  resolveAsyncComponent,
-  createAsyncPlaceholder,
-  extractPropsFromVNodeData
-} from './helpers/index'
-
-import {
-  callHook,
-  activeInstance,
-  updateChildComponent,
-  activateChildComponent,
-  deactivateChildComponent
-} from '../instance/lifecycle'
-
-import type {
-  MountedComponentVNode,
-  VNodeData,
-  VNodeWithData
-} from 'types/vnode'
-import type { Component } from 'types/component'
-import type { ComponentOptions, InternalComponentOptions } from 'types/options'
-
-export function getComponentName(options: ComponentOptions) {
-  return options.name || options.__name || options._componentTag
-}
-
-// inline hooks to be invoked on component VNodes during patch
-const componentVNodeHooks = {
-  init(vnode: VNodeWithData, hydrating: boolean): boolean | void {
-    if (
-      vnode.componentInstance &&
-      !vnode.componentInstance._isDestroyed &&
-      vnode.data.keepAlive
-    ) {
-      // kept-alive components, treat as a patch
-      const mountedNode: any = vnode // work around flow
-      componentVNodeHooks.prepatch(mountedNode, mountedNode)
-    } else {
-      const child = (vnode.componentInstance = createComponentInstanceForVnode(
-        vnode,
-        activeInstance
-      ))
-      child.$mount(hydrating ? vnode.elm : undefined, hydrating)
-    }
-  },
-
-  prepatch(oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
-    const options = vnode.componentOptions
-    const child = (vnode.componentInstance = oldVnode.componentInstance)
-    updateChildComponent(
-      child,
-      options.propsData, // updated props
-      options.listeners, // updated listeners
-      vnode, // new parent vnode
-      options.children // new children
-    )
-  },
-
-  insert(vnode: MountedComponentVNode) {
-    const { context, componentInstance } = vnode
-    if (!componentInstance._isMounted) {
-      componentInstance._isMounted = true
-      callHook(componentInstance, 'mounted')
-    }
-    if (vnode.data.keepAlive) {
-      if (context._isMounted) {
-        // vue-router#1212
-        // During updates, a kept-alive component's child components may
-        // change, so directly walking the tree here may call activated hooks
-        // on incorrect children. Instead we push them into a queue which will
-        // be processed after the whole patch process ended.
-        queueActivatedComponent(componentInstance)
-      } else {
-        activateChildComponent(componentInstance, true /* direct */)
-      }
-    }
-  },
-
-  destroy(vnode: MountedComponentVNode) {
-    const { componentInstance } = vnode
-    if (!componentInstance._isDestroyed) {
-      if (!vnode.data.keepAlive) {
-        componentInstance.$destroy()
-      } else {
-        deactivateChildComponent(componentInstance, true /* direct */)
-      }
-    }
-  }
-}
-
-const hooksToMerge = Object.keys(componentVNodeHooks)
-
-export function createComponent(
-  Ctor: typeof Component | Function | ComponentOptions | void,
-  data: VNodeData | undefined,
-  context: Component,
-  children?: Array<VNode>,
-  tag?: string
-): VNode | Array<VNode> | void {
-  if (isUndef(Ctor)) {
-    return
-  }
-
-  const baseCtor = context.$options._base
-
-  // plain options object: turn it into a constructor
-  if (isObject(Ctor)) {
-    Ctor = baseCtor.extend(Ctor as typeof Component)
-  }
-
-  // if at this stage it's not a constructor or an async component factory,
-  // reject.
-  if (typeof Ctor !== 'function') {
-    if (__DEV__) {
-      warn(`Invalid Component definition: ${String(Ctor)}`, context)
-    }
-    return
-  }
-
-  // async component
-  let asyncFactory
-  // @ts-expect-error
-  if (isUndef(Ctor.cid)) {
-    asyncFactory = Ctor
-    Ctor = resolveAsyncComponent(asyncFactory, baseCtor)
-    if (Ctor === undefined) {
-      // return a placeholder node for async component, which is rendered
-      // as a comment node but preserves all the raw information for the node.
-      // the information will be used for async server-rendering and hydration.
-      return createAsyncPlaceholder(asyncFactory, data, context, children, tag)
-    }
-  }
-
-  data = data || {}
-
-  // resolve constructor options in case global mixins are applied after
-  // component constructor creation
-  resolveConstructorOptions(Ctor as typeof Component)
-
-  // transform component v-model data into props & events
-  if (isDef(data.model)) {
-    // @ts-expect-error
-    transformModel(Ctor.options, data)
-  }
-
-  // extract props
-  // @ts-expect-error
-  const propsData = extractPropsFromVNodeData(data, Ctor, tag)
-
-  // functional component
-  // @ts-expect-error
-  if (isTrue(Ctor.options.functional)) {
-    return createFunctionalComponent(
-      Ctor as typeof Component,
-      propsData,
-      data,
-      context,
-      children
-    )
-  }
-
-  // extract listeners, since these needs to be treated as
-  // child component listeners instead of DOM listeners
-  const listeners = data.on
-  // replace with listeners with .native modifier
-  // so it gets processed during parent component patch.
-  data.on = data.nativeOn
-
-  // @ts-expect-error
-  if (isTrue(Ctor.options.abstract)) {
-    // abstract components do not keep anything
-    // other than props & listeners & slot
-
-    // work around flow
-    const slot = data.slot
-    data = {}
-    if (slot) {
-      data.slot = slot
-    }
-  }
-
-  // install component management hooks onto the placeholder node
-  installComponentHooks(data)
-
-  // return a placeholder vnode
-  // @ts-expect-error
-  const name = getComponentName(Ctor.options) || tag
-  const vnode = new VNode(
-    // @ts-expect-error
-    `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
-    data,
-    undefined,
-    undefined,
-    undefined,
-    context,
-    // @ts-expect-error
-    { Ctor, propsData, listeners, tag, children },
-    asyncFactory
-  )
-
-  return vnode
-}
-
-export function createComponentInstanceForVnode(
-  // we know it's MountedComponentVNode but flow doesn't
-  vnode: any,
-  // activeInstance in lifecycle state
-  parent?: any
-): Component {
-  const options: InternalComponentOptions = {
-    _isComponent: true,
-    _parentVnode: vnode,
-    parent
-  }
-  // check inline-template render functions
-  const inlineTemplate = vnode.data.inlineTemplate
-  if (isDef(inlineTemplate)) {
-    options.render = inlineTemplate.render
-    options.staticRenderFns = inlineTemplate.staticRenderFns
-  }
-  return new vnode.componentOptions.Ctor(options)
-}
-
-function installComponentHooks(data: VNodeData) {
-  const hooks = data.hook || (data.hook = {})
-  for (let i = 0; i < hooksToMerge.length; i++) {
-    const key = hooksToMerge[i]
-    const existing = hooks[key]
-    const toMerge = componentVNodeHooks[key]
-    // @ts-expect-error
-    if (existing !== toMerge && !(existing && existing._merged)) {
-      hooks[key] = existing ? mergeHook(toMerge, existing) : toMerge
-    }
-  }
-}
-
-function mergeHook(f1: any, f2: any): Function {
-  const merged = (a, b) => {
-    // flow complains about extra args which is why we use any
-    f1(a, b)
-    f2(a, b)
-  }
-  merged._merged = true
-  return merged
-}
-
-// transform component v-model info (value and callback) into
-// prop and event handler respectively.
-function transformModel(options, data: any) {
-  const prop = (options.model && options.model.prop) || 'value'
-  const event = (options.model && options.model.event) || 'input'
-  ;(data.attrs || (data.attrs = {}))[prop] = data.model.value
-  const on = data.on || (data.on = {})
-  const existing = on[event]
-  const callback = data.model.callback
-  if (isDef(existing)) {
-    if (
-      isArray(existing)
-        ? existing.indexOf(callback) === -1
-        : existing !== callback
-    ) {
-      on[event] = [callback].concat(existing)
-    }
-  } else {
-    on[event] = callback
-  }
-}
diff --git a/src/core/vdom/create-element.ts b/src/core/vdom/create-element.ts
deleted file mode 100644
index 62dd004c34a..00000000000
--- a/src/core/vdom/create-element.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-import config from '../config'
-import VNode, { createEmptyVNode } from './vnode'
-import { createComponent } from './create-component'
-import { traverse } from '../observer/traverse'
-
-import {
-  warn,
-  isDef,
-  isUndef,
-  isArray,
-  isTrue,
-  isObject,
-  isPrimitive,
-  resolveAsset,
-  isFunction
-} from '../util/index'
-
-import { normalizeChildren, simpleNormalizeChildren } from './helpers/index'
-import type { Component } from 'types/component'
-import type { VNodeData } from 'types/vnode'
-
-const SIMPLE_NORMALIZE = 1
-const ALWAYS_NORMALIZE = 2
-
-// wrapper function for providing a more flexible interface
-// without getting yelled at by flow
-export function createElement(
-  context: Component,
-  tag: any,
-  data: any,
-  children: any,
-  normalizationType: any,
-  alwaysNormalize: boolean
-): VNode | Array<VNode> {
-  if (isArray(data) || isPrimitive(data)) {
-    normalizationType = children
-    children = data
-    data = undefined
-  }
-  if (isTrue(alwaysNormalize)) {
-    normalizationType = ALWAYS_NORMALIZE
-  }
-  return _createElement(context, tag, data, children, normalizationType)
-}
-
-export function _createElement(
-  context: Component,
-  tag?: string | Component | Function | Object,
-  data?: VNodeData,
-  children?: any,
-  normalizationType?: number
-): VNode | Array<VNode> {
-  if (isDef(data) && isDef((data as any).__ob__)) {
-    __DEV__ &&
-      warn(
-        `Avoid using observed data object as vnode data: ${JSON.stringify(
-          data
-        )}\n` + 'Always create fresh vnode data objects in each render!',
-        context
-      )
-    return createEmptyVNode()
-  }
-  // object syntax in v-bind
-  if (isDef(data) && isDef(data.is)) {
-    tag = data.is
-  }
-  if (!tag) {
-    // in case of component :is set to falsy value
-    return createEmptyVNode()
-  }
-  // warn against non-primitive key
-  if (__DEV__ && isDef(data) && isDef(data.key) && !isPrimitive(data.key)) {
-    warn(
-      'Avoid using non-primitive value as key, ' +
-        'use string/number value instead.',
-      context
-    )
-  }
-  // support single function children as default scoped slot
-  if (isArray(children) && isFunction(children[0])) {
-    data = data || {}
-    data.scopedSlots = { default: children[0] }
-    children.length = 0
-  }
-  if (normalizationType === ALWAYS_NORMALIZE) {
-    children = normalizeChildren(children)
-  } else if (normalizationType === SIMPLE_NORMALIZE) {
-    children = simpleNormalizeChildren(children)
-  }
-  let vnode, ns
-  if (typeof tag === 'string') {
-    let Ctor
-    ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
-    if (config.isReservedTag(tag)) {
-      // platform built-in elements
-      if (
-        __DEV__ &&
-        isDef(data) &&
-        isDef(data.nativeOn) &&
-        data.tag !== 'component'
-      ) {
-        warn(
-          `The .native modifier for v-on is only valid on components but it was used on <${tag}>.`,
-          context
-        )
-      }
-      vnode = new VNode(
-        config.parsePlatformTagName(tag),
-        data,
-        children,
-        undefined,
-        undefined,
-        context
-      )
-    } else if (
-      (!data || !data.pre) &&
-      isDef((Ctor = resolveAsset(context.$options, 'components', tag)))
-    ) {
-      // component
-      vnode = createComponent(Ctor, data, context, children, tag)
-    } else {
-      // unknown or unlisted namespaced elements
-      // check at runtime because it may get assigned a namespace when its
-      // parent normalizes children
-      vnode = new VNode(tag, data, children, undefined, undefined, context)
-    }
-  } else {
-    // direct component options / constructor
-    vnode = createComponent(tag as any, data, context, children)
-  }
-  if (isArray(vnode)) {
-    return vnode
-  } else if (isDef(vnode)) {
-    if (isDef(ns)) applyNS(vnode, ns)
-    if (isDef(data)) registerDeepBindings(data)
-    return vnode
-  } else {
-    return createEmptyVNode()
-  }
-}
-
-function applyNS(vnode, ns, force?: boolean) {
-  vnode.ns = ns
-  if (vnode.tag === 'foreignObject') {
-    // use default namespace inside foreignObject
-    ns = undefined
-    force = true
-  }
-  if (isDef(vnode.children)) {
-    for (let i = 0, l = vnode.children.length; i < l; i++) {
-      const child = vnode.children[i]
-      if (
-        isDef(child.tag) &&
-        (isUndef(child.ns) || (isTrue(force) && child.tag !== 'svg'))
-      ) {
-        applyNS(child, ns, force)
-      }
-    }
-  }
-}
-
-// ref #5318
-// necessary to ensure parent re-render when deep bindings like :style and
-// :class are used on slot nodes
-function registerDeepBindings(data) {
-  if (isObject(data.style)) {
-    traverse(data.style)
-  }
-  if (isObject(data.class)) {
-    traverse(data.class)
-  }
-}
diff --git a/src/core/vdom/create-functional-component.ts b/src/core/vdom/create-functional-component.ts
deleted file mode 100644
index 55bc5bd1ba3..00000000000
--- a/src/core/vdom/create-functional-component.ts
+++ /dev/null
@@ -1,180 +0,0 @@
-import VNode, { cloneVNode } from './vnode'
-import { createElement } from './create-element'
-import { resolveInject } from '../instance/inject'
-import { normalizeChildren } from '../vdom/helpers/normalize-children'
-import { resolveSlots } from '../instance/render-helpers/resolve-slots'
-import { normalizeScopedSlots } from '../vdom/helpers/normalize-scoped-slots'
-import { installRenderHelpers } from '../instance/render-helpers/index'
-
-import {
-  isDef,
-  isTrue,
-  hasOwn,
-  isArray,
-  camelize,
-  emptyObject,
-  validateProp
-} from '../util/index'
-import type { Component } from 'types/component'
-import type { VNodeData } from 'types/vnode'
-
-export function FunctionalRenderContext(
-  data: VNodeData,
-  props: Object,
-  children: Array<VNode> | undefined,
-  parent: Component,
-  Ctor: typeof Component
-) {
-  const options = Ctor.options
-  // ensure the createElement function in functional components
-  // gets a unique context - this is necessary for correct named slot check
-  let contextVm
-  if (hasOwn(parent, '_uid')) {
-    contextVm = Object.create(parent)
-    contextVm._original = parent
-  } else {
-    // the context vm passed in is a functional context as well.
-    // in this case we want to make sure we are able to get a hold to the
-    // real context instance.
-    contextVm = parent
-    // @ts-ignore
-    parent = parent._original
-  }
-  const isCompiled = isTrue(options._compiled)
-  const needNormalization = !isCompiled
-
-  this.data = data
-  this.props = props
-  this.children = children
-  this.parent = parent
-  this.listeners = data.on || emptyObject
-  this.injections = resolveInject(options.inject, parent)
-  this.slots = () => {
-    if (!this.$slots) {
-      normalizeScopedSlots(
-        parent,
-        data.scopedSlots,
-        (this.$slots = resolveSlots(children, parent))
-      )
-    }
-    return this.$slots
-  }
-
-  Object.defineProperty(this, 'scopedSlots', {
-    enumerable: true,
-    get() {
-      return normalizeScopedSlots(parent, data.scopedSlots, this.slots())
-    }
-  } as any)
-
-  // support for compiled functional template
-  if (isCompiled) {
-    // exposing $options for renderStatic()
-    this.$options = options
-    // pre-resolve slots for renderSlot()
-    this.$slots = this.slots()
-    this.$scopedSlots = normalizeScopedSlots(
-      parent,
-      data.scopedSlots,
-      this.$slots
-    )
-  }
-
-  if (options._scopeId) {
-    this._c = (a, b, c, d) => {
-      const vnode = createElement(contextVm, a, b, c, d, needNormalization)
-      if (vnode && !isArray(vnode)) {
-        vnode.fnScopeId = options._scopeId
-        vnode.fnContext = parent
-      }
-      return vnode
-    }
-  } else {
-    this._c = (a, b, c, d) =>
-      createElement(contextVm, a, b, c, d, needNormalization)
-  }
-}
-
-installRenderHelpers(FunctionalRenderContext.prototype)
-
-export function createFunctionalComponent(
-  Ctor: typeof Component,
-  propsData: Object | undefined,
-  data: VNodeData,
-  contextVm: Component,
-  children?: Array<VNode>
-): VNode | Array<VNode> | void {
-  const options = Ctor.options
-  const props = {}
-  const propOptions = options.props
-  if (isDef(propOptions)) {
-    for (const key in propOptions) {
-      props[key] = validateProp(key, propOptions, propsData || emptyObject)
-    }
-  } else {
-    if (isDef(data.attrs)) mergeProps(props, data.attrs)
-    if (isDef(data.props)) mergeProps(props, data.props)
-  }
-
-  const renderContext = new FunctionalRenderContext(
-    data,
-    props,
-    children,
-    contextVm,
-    Ctor
-  )
-
-  const vnode = options.render.call(null, renderContext._c, renderContext)
-
-  if (vnode instanceof VNode) {
-    return cloneAndMarkFunctionalResult(
-      vnode,
-      data,
-      renderContext.parent,
-      options,
-      renderContext
-    )
-  } else if (isArray(vnode)) {
-    const vnodes = normalizeChildren(vnode) || []
-    const res = new Array(vnodes.length)
-    for (let i = 0; i < vnodes.length; i++) {
-      res[i] = cloneAndMarkFunctionalResult(
-        vnodes[i],
-        data,
-        renderContext.parent,
-        options,
-        renderContext
-      )
-    }
-    return res
-  }
-}
-
-function cloneAndMarkFunctionalResult(
-  vnode,
-  data,
-  contextVm,
-  options,
-  renderContext
-) {
-  // #7817 clone node before setting fnContext, otherwise if the node is reused
-  // (e.g. it was from a cached normal slot) the fnContext causes named slots
-  // that should not be matched to match.
-  const clone = cloneVNode(vnode)
-  clone.fnContext = contextVm
-  clone.fnOptions = options
-  if (__DEV__) {
-    ;(clone.devtoolsMeta = clone.devtoolsMeta || ({} as any)).renderContext =
-      renderContext
-  }
-  if (data.slot) {
-    ;(clone.data || (clone.data = {})).slot = data.slot
-  }
-  return clone
-}
-
-function mergeProps(to, from) {
-  for (const key in from) {
-    to[camelize(key)] = from[key]
-  }
-}
diff --git a/src/core/vdom/helpers/extract-props.ts b/src/core/vdom/helpers/extract-props.ts
deleted file mode 100644
index a8015aa6e75..00000000000
--- a/src/core/vdom/helpers/extract-props.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import {
-  tip,
-  hasOwn,
-  isDef,
-  isUndef,
-  hyphenate,
-  formatComponentName
-} from 'core/util/index'
-import type { Component } from 'types/component'
-import type { VNodeData } from 'types/vnode'
-
-export function extractPropsFromVNodeData(
-  data: VNodeData,
-  Ctor: typeof Component,
-  tag?: string
-): object | undefined {
-  // we are only extracting raw values here.
-  // validation and default values are handled in the child
-  // component itself.
-  const propOptions = Ctor.options.props
-  if (isUndef(propOptions)) {
-    return
-  }
-  const res = {}
-  const { attrs, props } = data
-  if (isDef(attrs) || isDef(props)) {
-    for (const key in propOptions) {
-      const altKey = hyphenate(key)
-      if (__DEV__) {
-        const keyInLowerCase = key.toLowerCase()
-        if (key !== keyInLowerCase && attrs && hasOwn(attrs, keyInLowerCase)) {
-          tip(
-            `Prop "${keyInLowerCase}" is passed to component ` +
-              `${formatComponentName(
-                // @ts-expect-error tag is string
-                tag || Ctor
-              )}, but the declared prop name is` +
-              ` "${key}". ` +
-              `Note that HTML attributes are case-insensitive and camelCased ` +
-              `props need to use their kebab-case equivalents when using in-DOM ` +
-              `templates. You should probably use "${altKey}" instead of "${key}".`
-          )
-        }
-      }
-      checkProp(res, props, key, altKey, true) ||
-        checkProp(res, attrs, key, altKey, false)
-    }
-  }
-  return res
-}
-
-function checkProp(
-  res: Object,
-  hash: Object | undefined,
-  key: string,
-  altKey: string,
-  preserve: boolean
-): boolean {
-  if (isDef(hash)) {
-    if (hasOwn(hash, key)) {
-      res[key] = hash[key]
-      if (!preserve) {
-        delete hash[key]
-      }
-      return true
-    } else if (hasOwn(hash, altKey)) {
-      res[key] = hash[altKey]
-      if (!preserve) {
-        delete hash[altKey]
-      }
-      return true
-    }
-  }
-  return false
-}
diff --git a/src/core/vdom/helpers/get-first-component-child.ts b/src/core/vdom/helpers/get-first-component-child.ts
deleted file mode 100644
index ab4543af3eb..00000000000
--- a/src/core/vdom/helpers/get-first-component-child.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { isDef, isArray } from 'shared/util'
-import VNode from '../vnode'
-import { isAsyncPlaceholder } from './is-async-placeholder'
-
-export function getFirstComponentChild(
-  children?: Array<VNode>
-): VNode | undefined {
-  if (isArray(children)) {
-    for (let i = 0; i < children.length; i++) {
-      const c = children[i]
-      if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {
-        return c
-      }
-    }
-  }
-}
diff --git a/src/core/vdom/helpers/index.ts b/src/core/vdom/helpers/index.ts
deleted file mode 100644
index 12a72ee848b..00000000000
--- a/src/core/vdom/helpers/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export * from './merge-hook'
-export * from './extract-props'
-export * from './update-listeners'
-export * from './normalize-children'
-export * from './resolve-async-component'
-export * from './get-first-component-child'
-export * from './is-async-placeholder'
diff --git a/src/core/vdom/helpers/is-async-placeholder.ts b/src/core/vdom/helpers/is-async-placeholder.ts
deleted file mode 100644
index 6e0d602758b..00000000000
--- a/src/core/vdom/helpers/is-async-placeholder.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import VNode from '../vnode'
-
-export function isAsyncPlaceholder(node: VNode): boolean {
-  // @ts-expect-error not really boolean type
-  return node.isComment && node.asyncFactory
-}
diff --git a/src/core/vdom/helpers/merge-hook.ts b/src/core/vdom/helpers/merge-hook.ts
deleted file mode 100644
index 6881f914170..00000000000
--- a/src/core/vdom/helpers/merge-hook.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import VNode from '../vnode'
-import { createFnInvoker } from './update-listeners'
-import { remove, isDef, isUndef, isTrue } from 'shared/util'
-
-export function mergeVNodeHook(
-  def: Record<string, any>,
-  hookKey: string,
-  hook: Function
-) {
-  if (def instanceof VNode) {
-    def = def.data!.hook || (def.data!.hook = {})
-  }
-  let invoker
-  const oldHook = def[hookKey]
-
-  function wrappedHook() {
-    hook.apply(this, arguments)
-    // important: remove merged hook to ensure it's called only once
-    // and prevent memory leak
-    remove(invoker.fns, wrappedHook)
-  }
-
-  if (isUndef(oldHook)) {
-    // no existing hook
-    invoker = createFnInvoker([wrappedHook])
-  } else {
-    /* istanbul ignore if */
-    if (isDef(oldHook.fns) && isTrue(oldHook.merged)) {
-      // already a merged invoker
-      invoker = oldHook
-      invoker.fns.push(wrappedHook)
-    } else {
-      // existing plain hook
-      invoker = createFnInvoker([oldHook, wrappedHook])
-    }
-  }
-
-  invoker.merged = true
-  def[hookKey] = invoker
-}
diff --git a/src/core/vdom/helpers/normalize-children.ts b/src/core/vdom/helpers/normalize-children.ts
deleted file mode 100644
index 6908ab1d15d..00000000000
--- a/src/core/vdom/helpers/normalize-children.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-import VNode, { createTextVNode } from 'core/vdom/vnode'
-import {
-  isFalse,
-  isTrue,
-  isArray,
-  isDef,
-  isUndef,
-  isPrimitive
-} from 'shared/util'
-
-// The template compiler attempts to minimize the need for normalization by
-// statically analyzing the template at compile time.
-//
-// For plain HTML markup, normalization can be completely skipped because the
-// generated render function is guaranteed to return Array<VNode>. There are
-// two cases where extra normalization is needed:
-
-// 1. When the children contains components - because a functional component
-// may return an Array instead of a single root. In this case, just a simple
-// normalization is needed - if any child is an Array, we flatten the whole
-// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep
-// because functional components already normalize their own children.
-export function simpleNormalizeChildren(children: any) {
-  for (let i = 0; i < children.length; i++) {
-    if (isArray(children[i])) {
-      return Array.prototype.concat.apply([], children)
-    }
-  }
-  return children
-}
-
-// 2. When the children contains constructs that always generated nested Arrays,
-// e.g. <template>, <slot>, v-for, or when the children is provided by user
-// with hand-written render functions / JSX. In such cases a full normalization
-// is needed to cater to all possible types of children values.
-export function normalizeChildren(children: any): Array<VNode> | undefined {
-  return isPrimitive(children)
-    ? [createTextVNode(children)]
-    : isArray(children)
-    ? normalizeArrayChildren(children)
-    : undefined
-}
-
-function isTextNode(node): boolean {
-  return isDef(node) && isDef(node.text) && isFalse(node.isComment)
-}
-
-function normalizeArrayChildren(
-  children: any,
-  nestedIndex?: string
-): Array<VNode> {
-  const res: VNode[] = []
-  let i, c, lastIndex, last
-  for (i = 0; i < children.length; i++) {
-    c = children[i]
-    if (isUndef(c) || typeof c === 'boolean') continue
-    lastIndex = res.length - 1
-    last = res[lastIndex]
-    //  nested
-    if (isArray(c)) {
-      if (c.length > 0) {
-        c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`)
-        // merge adjacent text nodes
-        if (isTextNode(c[0]) && isTextNode(last)) {
-          res[lastIndex] = createTextVNode(last.text + c[0].text)
-          c.shift()
-        }
-        res.push.apply(res, c)
-      }
-    } else if (isPrimitive(c)) {
-      if (isTextNode(last)) {
-        // merge adjacent text nodes
-        // this is necessary for SSR hydration because text nodes are
-        // essentially merged when rendered to HTML strings
-        res[lastIndex] = createTextVNode(last.text + c)
-      } else if (c !== '') {
-        // convert primitive to vnode
-        res.push(createTextVNode(c))
-      }
-    } else {
-      if (isTextNode(c) && isTextNode(last)) {
-        // merge adjacent text nodes
-        res[lastIndex] = createTextVNode(last.text + c.text)
-      } else {
-        // default key for nested array children (likely generated by v-for)
-        if (
-          isTrue(children._isVList) &&
-          isDef(c.tag) &&
-          isUndef(c.key) &&
-          isDef(nestedIndex)
-        ) {
-          c.key = `__vlist${nestedIndex}_${i}__`
-        }
-        res.push(c)
-      }
-    }
-  }
-  return res
-}
diff --git a/src/core/vdom/helpers/normalize-scoped-slots.ts b/src/core/vdom/helpers/normalize-scoped-slots.ts
deleted file mode 100644
index 10a0d1186e1..00000000000
--- a/src/core/vdom/helpers/normalize-scoped-slots.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import { def } from 'core/util/lang'
-import { normalizeChildren } from 'core/vdom/helpers/normalize-children'
-import { emptyObject, isArray } from 'shared/util'
-import { isAsyncPlaceholder } from './is-async-placeholder'
-import type VNode from '../vnode'
-import { Component } from 'types/component'
-import { currentInstance, setCurrentInstance } from 'v3/currentInstance'
-
-export function normalizeScopedSlots(
-  ownerVm: Component,
-  scopedSlots: { [key: string]: Function } | undefined,
-  normalSlots: { [key: string]: VNode[] },
-  prevScopedSlots?: { [key: string]: Function }
-): any {
-  let res
-  const hasNormalSlots = Object.keys(normalSlots).length > 0
-  const isStable = scopedSlots ? !!scopedSlots.$stable : !hasNormalSlots
-  const key = scopedSlots && scopedSlots.$key
-  if (!scopedSlots) {
-    res = {}
-  } else if (scopedSlots._normalized) {
-    // fast path 1: child component re-render only, parent did not change
-    return scopedSlots._normalized
-  } else if (
-    isStable &&
-    prevScopedSlots &&
-    prevScopedSlots !== emptyObject &&
-    key === prevScopedSlots.$key &&
-    !hasNormalSlots &&
-    !prevScopedSlots.$hasNormal
-  ) {
-    // fast path 2: stable scoped slots w/ no normal slots to proxy,
-    // only need to normalize once
-    return prevScopedSlots
-  } else {
-    res = {}
-    for (const key in scopedSlots) {
-      if (scopedSlots[key] && key[0] !== '$') {
-        res[key] = normalizeScopedSlot(
-          ownerVm,
-          normalSlots,
-          key,
-          scopedSlots[key]
-        )
-      }
-    }
-  }
-  // expose normal slots on scopedSlots
-  for (const key in normalSlots) {
-    if (!(key in res)) {
-      res[key] = proxyNormalSlot(normalSlots, key)
-    }
-  }
-  // avoriaz seems to mock a non-extensible $scopedSlots object
-  // and when that is passed down this would cause an error
-  if (scopedSlots && Object.isExtensible(scopedSlots)) {
-    scopedSlots._normalized = res
-  }
-  def(res, '$stable', isStable)
-  def(res, '$key', key)
-  def(res, '$hasNormal', hasNormalSlots)
-  return res
-}
-
-function normalizeScopedSlot(vm, normalSlots, key, fn) {
-  const normalized = function () {
-    const cur = currentInstance
-    setCurrentInstance(vm)
-    let res = arguments.length ? fn.apply(null, arguments) : fn({})
-    res =
-      res && typeof res === 'object' && !isArray(res)
-        ? [res] // single vnode
-        : normalizeChildren(res)
-    const vnode: VNode | null = res && res[0]
-    setCurrentInstance(cur)
-    return res &&
-      (!vnode ||
-        (res.length === 1 && vnode.isComment && !isAsyncPlaceholder(vnode))) // #9658, #10391
-      ? undefined
-      : res
-  }
-  // this is a slot using the new v-slot syntax without scope. although it is
-  // compiled as a scoped slot, render fn users would expect it to be present
-  // on this.$slots because the usage is semantically a normal slot.
-  if (fn.proxy) {
-    Object.defineProperty(normalSlots, key, {
-      get: normalized,
-      enumerable: true,
-      configurable: true
-    })
-  }
-  return normalized
-}
-
-function proxyNormalSlot(slots, key) {
-  return () => slots[key]
-}
diff --git a/src/core/vdom/helpers/resolve-async-component.ts b/src/core/vdom/helpers/resolve-async-component.ts
deleted file mode 100644
index b7c66ae6459..00000000000
--- a/src/core/vdom/helpers/resolve-async-component.ts
+++ /dev/null
@@ -1,157 +0,0 @@
-import {
-  warn,
-  once,
-  isDef,
-  isUndef,
-  isTrue,
-  isObject,
-  hasSymbol,
-  isPromise,
-  remove
-} from 'core/util/index'
-
-import VNode, { createEmptyVNode } from 'core/vdom/vnode'
-import { currentRenderingInstance } from 'core/instance/render'
-import type { VNodeData } from 'types/vnode'
-import type { Component } from 'types/component'
-
-function ensureCtor(comp: any, base) {
-  if (comp.__esModule || (hasSymbol && comp[Symbol.toStringTag] === 'Module')) {
-    comp = comp.default
-  }
-  return isObject(comp) ? base.extend(comp) : comp
-}
-
-export function createAsyncPlaceholder(
-  factory: Function,
-  data: VNodeData | undefined,
-  context: Component,
-  children: Array<VNode> | undefined,
-  tag?: string
-): VNode {
-  const node = createEmptyVNode()
-  node.asyncFactory = factory
-  node.asyncMeta = { data, context, children, tag }
-  return node
-}
-
-export function resolveAsyncComponent(
-  factory: { (...args: any[]): any; [keye: string]: any },
-  baseCtor: typeof Component
-): typeof Component | void {
-  if (isTrue(factory.error) && isDef(factory.errorComp)) {
-    return factory.errorComp
-  }
-
-  if (isDef(factory.resolved)) {
-    return factory.resolved
-  }
-
-  const owner = currentRenderingInstance
-  if (owner && isDef(factory.owners) && factory.owners.indexOf(owner) === -1) {
-    // already pending
-    factory.owners.push(owner)
-  }
-
-  if (isTrue(factory.loading) && isDef(factory.loadingComp)) {
-    return factory.loadingComp
-  }
-
-  if (owner && !isDef(factory.owners)) {
-    const owners = (factory.owners = [owner])
-    let sync = true
-    let timerLoading: number | null = null
-    let timerTimeout: number | null = null
-
-    owner.$on('hook:destroyed', () => remove(owners, owner))
-
-    const forceRender = (renderCompleted: boolean) => {
-      for (let i = 0, l = owners.length; i < l; i++) {
-        owners[i].$forceUpdate()
-      }
-
-      if (renderCompleted) {
-        owners.length = 0
-        if (timerLoading !== null) {
-          clearTimeout(timerLoading)
-          timerLoading = null
-        }
-        if (timerTimeout !== null) {
-          clearTimeout(timerTimeout)
-          timerTimeout = null
-        }
-      }
-    }
-
-    const resolve = once((res: Object | Component) => {
-      // cache resolved
-      factory.resolved = ensureCtor(res, baseCtor)
-      // invoke callbacks only if this is not a synchronous resolve
-      // (async resolves are shimmed as synchronous during SSR)
-      if (!sync) {
-        forceRender(true)
-      } else {
-        owners.length = 0
-      }
-    })
-
-    const reject = once(reason => {
-      __DEV__ &&
-        warn(
-          `Failed to resolve async component: ${String(factory)}` +
-            (reason ? `\nReason: ${reason}` : '')
-        )
-      if (isDef(factory.errorComp)) {
-        factory.error = true
-        forceRender(true)
-      }
-    })
-
-    const res = factory(resolve, reject)
-
-    if (isObject(res)) {
-      if (isPromise(res)) {
-        // () => Promise
-        if (isUndef(factory.resolved)) {
-          res.then(resolve, reject)
-        }
-      } else if (isPromise(res.component)) {
-        res.component.then(resolve, reject)
-
-        if (isDef(res.error)) {
-          factory.errorComp = ensureCtor(res.error, baseCtor)
-        }
-
-        if (isDef(res.loading)) {
-          factory.loadingComp = ensureCtor(res.loading, baseCtor)
-          if (res.delay === 0) {
-            factory.loading = true
-          } else {
-            // @ts-expect-error NodeJS timeout type
-            timerLoading = setTimeout(() => {
-              timerLoading = null
-              if (isUndef(factory.resolved) && isUndef(factory.error)) {
-                factory.loading = true
-                forceRender(false)
-              }
-            }, res.delay || 200)
-          }
-        }
-
-        if (isDef(res.timeout)) {
-          // @ts-expect-error NodeJS timeout type
-          timerTimeout = setTimeout(() => {
-            timerTimeout = null
-            if (isUndef(factory.resolved)) {
-              reject(__DEV__ ? `timeout (${res.timeout}ms)` : null)
-            }
-          }, res.timeout)
-        }
-      }
-    }
-
-    sync = false
-    // return in case resolved synchronously
-    return factory.loading ? factory.loadingComp : factory.resolved
-  }
-}
diff --git a/src/core/vdom/helpers/update-listeners.ts b/src/core/vdom/helpers/update-listeners.ts
deleted file mode 100644
index f7c3c03efb4..00000000000
--- a/src/core/vdom/helpers/update-listeners.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { warn, invokeWithErrorHandling } from 'core/util/index'
-import { cached, isUndef, isTrue, isArray } from 'shared/util'
-import type { Component } from 'types/component'
-
-const normalizeEvent = cached(
-  (
-    name: string
-  ): {
-    name: string
-    once: boolean
-    capture: boolean
-    passive: boolean
-    handler?: Function
-    params?: Array<any>
-  } => {
-    const passive = name.charAt(0) === '&'
-    name = passive ? name.slice(1) : name
-    const once = name.charAt(0) === '~' // Prefixed last, checked first
-    name = once ? name.slice(1) : name
-    const capture = name.charAt(0) === '!'
-    name = capture ? name.slice(1) : name
-    return {
-      name,
-      once,
-      capture,
-      passive
-    }
-  }
-)
-
-export function createFnInvoker(
-  fns: Function | Array<Function>,
-  vm?: Component
-): Function {
-  function invoker() {
-    const fns = invoker.fns
-    if (isArray(fns)) {
-      const cloned = fns.slice()
-      for (let i = 0; i < cloned.length; i++) {
-        invokeWithErrorHandling(
-          cloned[i],
-          null,
-          arguments as any,
-          vm,
-          `v-on handler`
-        )
-      }
-    } else {
-      // return handler return value for single handlers
-      return invokeWithErrorHandling(
-        fns,
-        null,
-        arguments as any,
-        vm,
-        `v-on handler`
-      )
-    }
-  }
-  invoker.fns = fns
-  return invoker
-}
-
-export function updateListeners(
-  on: Object,
-  oldOn: Object,
-  add: Function,
-  remove: Function,
-  createOnceHandler: Function,
-  vm: Component
-) {
-  let name, cur, old, event
-  for (name in on) {
-    cur = on[name]
-    old = oldOn[name]
-    event = normalizeEvent(name)
-    if (isUndef(cur)) {
-      __DEV__ &&
-        warn(
-          `Invalid handler for event "${event.name}": got ` + String(cur),
-          vm
-        )
-    } else if (isUndef(old)) {
-      if (isUndef(cur.fns)) {
-        cur = on[name] = createFnInvoker(cur, vm)
-      }
-      if (isTrue(event.once)) {
-        cur = on[name] = createOnceHandler(event.name, cur, event.capture)
-      }
-      add(event.name, cur, event.capture, event.passive, event.params)
-    } else if (cur !== old) {
-      old.fns = cur
-      on[name] = old
-    }
-  }
-  for (name in oldOn) {
-    if (isUndef(on[name])) {
-      event = normalizeEvent(name)
-      remove(event.name, oldOn[name], event.capture)
-    }
-  }
-}
diff --git a/src/core/vdom/modules/directives.ts b/src/core/vdom/modules/directives.ts
deleted file mode 100644
index 853b20021e7..00000000000
--- a/src/core/vdom/modules/directives.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import { emptyNode } from 'core/vdom/patch'
-import { resolveAsset, handleError } from 'core/util/index'
-import { mergeVNodeHook } from 'core/vdom/helpers/index'
-import type { VNodeDirective, VNodeWithData } from 'types/vnode'
-import type { Component } from 'types/component'
-
-export default {
-  create: updateDirectives,
-  update: updateDirectives,
-  destroy: function unbindDirectives(vnode: VNodeWithData) {
-    // @ts-expect-error emptyNode is not VNodeWithData
-    updateDirectives(vnode, emptyNode)
-  }
-}
-
-function updateDirectives(oldVnode: VNodeWithData, vnode: VNodeWithData) {
-  if (oldVnode.data.directives || vnode.data.directives) {
-    _update(oldVnode, vnode)
-  }
-}
-
-function _update(oldVnode, vnode) {
-  const isCreate = oldVnode === emptyNode
-  const isDestroy = vnode === emptyNode
-  const oldDirs = normalizeDirectives(
-    oldVnode.data.directives,
-    oldVnode.context
-  )
-  const newDirs = normalizeDirectives(vnode.data.directives, vnode.context)
-
-  const dirsWithInsert: any[] = []
-  const dirsWithPostpatch: any[] = []
-
-  let key, oldDir, dir
-  for (key in newDirs) {
-    oldDir = oldDirs[key]
-    dir = newDirs[key]
-    if (!oldDir) {
-      // new directive, bind
-      callHook(dir, 'bind', vnode, oldVnode)
-      if (dir.def && dir.def.inserted) {
-        dirsWithInsert.push(dir)
-      }
-    } else {
-      // existing directive, update
-      dir.oldValue = oldDir.value
-      dir.oldArg = oldDir.arg
-      callHook(dir, 'update', vnode, oldVnode)
-      if (dir.def && dir.def.componentUpdated) {
-        dirsWithPostpatch.push(dir)
-      }
-    }
-  }
-
-  if (dirsWithInsert.length) {
-    const callInsert = () => {
-      for (let i = 0; i < dirsWithInsert.length; i++) {
-        callHook(dirsWithInsert[i], 'inserted', vnode, oldVnode)
-      }
-    }
-    if (isCreate) {
-      mergeVNodeHook(vnode, 'insert', callInsert)
-    } else {
-      callInsert()
-    }
-  }
-
-  if (dirsWithPostpatch.length) {
-    mergeVNodeHook(vnode, 'postpatch', () => {
-      for (let i = 0; i < dirsWithPostpatch.length; i++) {
-        callHook(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode)
-      }
-    })
-  }
-
-  if (!isCreate) {
-    for (key in oldDirs) {
-      if (!newDirs[key]) {
-        // no longer present, unbind
-        callHook(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy)
-      }
-    }
-  }
-}
-
-const emptyModifiers = Object.create(null)
-
-function normalizeDirectives(
-  dirs: Array<VNodeDirective> | undefined,
-  vm: Component
-): { [key: string]: VNodeDirective } {
-  const res = Object.create(null)
-  if (!dirs) {
-    // $flow-disable-line
-    return res
-  }
-  let i: number, dir: VNodeDirective
-  for (i = 0; i < dirs.length; i++) {
-    dir = dirs[i]
-    if (!dir.modifiers) {
-      // $flow-disable-line
-      dir.modifiers = emptyModifiers
-    }
-    res[getRawDirName(dir)] = dir
-    if (vm._setupState && vm._setupState.__sfc) {
-      const setupDef = dir.def || resolveAsset(vm, '_setupState', 'v-' + dir.name)
-      if (typeof setupDef === 'function') {
-        dir.def = {
-          bind: setupDef,
-          update: setupDef,
-        }
-      } else {
-        dir.def = setupDef
-      }
-    }
-    dir.def = dir.def || resolveAsset(vm.$options, 'directives', dir.name, true)
-  }
-  // $flow-disable-line
-  return res
-}
-
-function getRawDirName(dir: VNodeDirective): string {
-  return (
-    dir.rawName || `${dir.name}.${Object.keys(dir.modifiers || {}).join('.')}`
-  )
-}
-
-function callHook(dir, hook, vnode, oldVnode, isDestroy?: any) {
-  const fn = dir.def && dir.def[hook]
-  if (fn) {
-    try {
-      fn(vnode.elm, dir, vnode, oldVnode, isDestroy)
-    } catch (e: any) {
-      handleError(e, vnode.context, `directive ${dir.name} ${hook} hook`)
-    }
-  }
-}
diff --git a/src/core/vdom/modules/index.ts b/src/core/vdom/modules/index.ts
deleted file mode 100644
index 327693d098c..00000000000
--- a/src/core/vdom/modules/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-import directives from './directives'
-import ref from './template-ref'
-
-export default [ref, directives]
diff --git a/src/core/vdom/modules/template-ref.ts b/src/core/vdom/modules/template-ref.ts
deleted file mode 100644
index 27461fd94ba..00000000000
--- a/src/core/vdom/modules/template-ref.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import {
-  remove,
-  isDef,
-  hasOwn,
-  isArray,
-  isFunction,
-  invokeWithErrorHandling,
-  warn
-} from 'core/util'
-import type { VNodeWithData } from 'types/vnode'
-import { Component } from 'types/component'
-import { isRef } from 'v3'
-
-export default {
-  create(_: any, vnode: VNodeWithData) {
-    registerRef(vnode)
-  },
-  update(oldVnode: VNodeWithData, vnode: VNodeWithData) {
-    if (oldVnode.data.ref !== vnode.data.ref) {
-      registerRef(oldVnode, true)
-      registerRef(vnode)
-    }
-  },
-  destroy(vnode: VNodeWithData) {
-    registerRef(vnode, true)
-  }
-}
-
-export function registerRef(vnode: VNodeWithData, isRemoval?: boolean) {
-  const ref = vnode.data.ref
-  if (!isDef(ref)) return
-
-  const vm = vnode.context
-  const refValue = vnode.componentInstance || vnode.elm
-  const value = isRemoval ? null : refValue
-  const $refsValue = isRemoval ? undefined : refValue
-
-  if (isFunction(ref)) {
-    invokeWithErrorHandling(ref, vm, [value], vm, `template ref function`)
-    return
-  }
-
-  const isFor = vnode.data.refInFor
-  const _isString = typeof ref === 'string' || typeof ref === 'number'
-  const _isRef = isRef(ref)
-  const refs = vm.$refs
-
-  if (_isString || _isRef) {
-    if (isFor) {
-      const existing = _isString ? refs[ref] : ref.value
-      if (isRemoval) {
-        isArray(existing) && remove(existing, refValue)
-      } else {
-        if (!isArray(existing)) {
-          if (_isString) {
-            refs[ref] = [refValue]
-            setSetupRef(vm, ref, refs[ref])
-          } else {
-            ref.value = [refValue]
-          }
-        } else if (!existing.includes(refValue)) {
-          existing.push(refValue)
-        }
-      }
-    } else if (_isString) {
-      if (isRemoval && refs[ref] !== refValue) {
-        return
-      }
-      refs[ref] = $refsValue
-      setSetupRef(vm, ref, value)
-    } else if (_isRef) {
-      if (isRemoval && ref.value !== refValue) {
-        return
-      }
-      ref.value = value
-    } else if (__DEV__) {
-      warn(`Invalid template ref type: ${typeof ref}`)
-    }
-  }
-}
-
-function setSetupRef(
-  { _setupState }: Component,
-  key: string | number,
-  val: any
-) {
-  if (_setupState && hasOwn(_setupState, key as string)) {
-    if (isRef(_setupState[key])) {
-      _setupState[key].value = val
-    } else {
-      _setupState[key] = val
-    }
-  }
-}
diff --git a/src/core/vdom/patch.ts b/src/core/vdom/patch.ts
deleted file mode 100644
index 173840787bc..00000000000
--- a/src/core/vdom/patch.ts
+++ /dev/null
@@ -1,907 +0,0 @@
-/**
- * Virtual DOM patching algorithm based on Snabbdom by
- * Simon Friis Vindum (@paldepind)
- * Licensed under the MIT License
- * https://github.com/paldepind/snabbdom/blob/master/LICENSE
- *
- * modified by Evan You (@yyx990803)
- *
- * Not type-checking this because this file is perf-critical and the cost
- * of making flow understand it is not worth it.
- */
-
-import VNode, { cloneVNode } from './vnode'
-import config from '../config'
-import { SSR_ATTR } from 'shared/constants'
-import { registerRef } from './modules/template-ref'
-import { traverse } from '../observer/traverse'
-import { activeInstance } from '../instance/lifecycle'
-import { isTextInputType } from 'web/util/element'
-
-import {
-  warn,
-  isDef,
-  isUndef,
-  isTrue,
-  isArray,
-  makeMap,
-  isRegExp,
-  isPrimitive
-} from '../util/index'
-
-export const emptyNode = new VNode('', {}, [])
-
-const hooks = ['create', 'activate', 'update', 'remove', 'destroy']
-
-function sameVnode(a, b) {
-  return (
-    a.key === b.key &&
-    a.asyncFactory === b.asyncFactory &&
-    ((a.tag === b.tag &&
-      a.isComment === b.isComment &&
-      isDef(a.data) === isDef(b.data) &&
-      sameInputType(a, b)) ||
-      (isTrue(a.isAsyncPlaceholder) && isUndef(b.asyncFactory.error)))
-  )
-}
-
-function sameInputType(a, b) {
-  if (a.tag !== 'input') return true
-  let i
-  const typeA = isDef((i = a.data)) && isDef((i = i.attrs)) && i.type
-  const typeB = isDef((i = b.data)) && isDef((i = i.attrs)) && i.type
-  return typeA === typeB || (isTextInputType(typeA) && isTextInputType(typeB))
-}
-
-function createKeyToOldIdx(children, beginIdx, endIdx) {
-  let i, key
-  const map = {}
-  for (i = beginIdx; i <= endIdx; ++i) {
-    key = children[i].key
-    if (isDef(key)) map[key] = i
-  }
-  return map
-}
-
-export function createPatchFunction(backend) {
-  let i, j
-  const cbs: any = {}
-
-  const { modules, nodeOps } = backend
-
-  for (i = 0; i < hooks.length; ++i) {
-    cbs[hooks[i]] = []
-    for (j = 0; j < modules.length; ++j) {
-      if (isDef(modules[j][hooks[i]])) {
-        cbs[hooks[i]].push(modules[j][hooks[i]])
-      }
-    }
-  }
-
-  function emptyNodeAt(elm) {
-    return new VNode(nodeOps.tagName(elm).toLowerCase(), {}, [], undefined, elm)
-  }
-
-  function createRmCb(childElm, listeners) {
-    function remove() {
-      if (--remove.listeners === 0) {
-        removeNode(childElm)
-      }
-    }
-    remove.listeners = listeners
-    return remove
-  }
-
-  function removeNode(el) {
-    const parent = nodeOps.parentNode(el)
-    // element may have already been removed due to v-html / v-text
-    if (isDef(parent)) {
-      nodeOps.removeChild(parent, el)
-    }
-  }
-
-  function isUnknownElement(vnode, inVPre) {
-    return (
-      !inVPre &&
-      !vnode.ns &&
-      !(
-        config.ignoredElements.length &&
-        config.ignoredElements.some(ignore => {
-          return isRegExp(ignore)
-            ? ignore.test(vnode.tag)
-            : ignore === vnode.tag
-        })
-      ) &&
-      config.isUnknownElement(vnode.tag)
-    )
-  }
-
-  let creatingElmInVPre = 0
-
-  function createElm(
-    vnode,
-    insertedVnodeQueue,
-    parentElm?: any,
-    refElm?: any,
-    nested?: any,
-    ownerArray?: any,
-    index?: any
-  ) {
-    if (isDef(vnode.elm) && isDef(ownerArray)) {
-      // This vnode was used in a previous render!
-      // now it's used as a new node, overwriting its elm would cause
-      // potential patch errors down the road when it's used as an insertion
-      // reference node. Instead, we clone the node on-demand before creating
-      // associated DOM element for it.
-      vnode = ownerArray[index] = cloneVNode(vnode)
-    }
-
-    vnode.isRootInsert = !nested // for transition enter check
-    if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
-      return
-    }
-
-    const data = vnode.data
-    const children = vnode.children
-    const tag = vnode.tag
-    if (isDef(tag)) {
-      if (__DEV__) {
-        if (data && data.pre) {
-          creatingElmInVPre++
-        }
-        if (isUnknownElement(vnode, creatingElmInVPre)) {
-          warn(
-            'Unknown custom element: <' +
-              tag +
-              '> - did you ' +
-              'register the component correctly? For recursive components, ' +
-              'make sure to provide the "name" option.',
-            vnode.context
-          )
-        }
-      }
-
-      vnode.elm = vnode.ns
-        ? nodeOps.createElementNS(vnode.ns, tag)
-        : nodeOps.createElement(tag, vnode)
-      setScope(vnode)
-
-      createChildren(vnode, children, insertedVnodeQueue)
-      if (isDef(data)) {
-        invokeCreateHooks(vnode, insertedVnodeQueue)
-      }
-      insert(parentElm, vnode.elm, refElm)
-
-      if (__DEV__ && data && data.pre) {
-        creatingElmInVPre--
-      }
-    } else if (isTrue(vnode.isComment)) {
-      vnode.elm = nodeOps.createComment(vnode.text)
-      insert(parentElm, vnode.elm, refElm)
-    } else {
-      vnode.elm = nodeOps.createTextNode(vnode.text)
-      insert(parentElm, vnode.elm, refElm)
-    }
-  }
-
-  function createComponent(vnode, insertedVnodeQueue, parentElm, refElm) {
-    let i = vnode.data
-    if (isDef(i)) {
-      const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
-      if (isDef((i = i.hook)) && isDef((i = i.init))) {
-        i(vnode, false /* hydrating */)
-      }
-      // after calling the init hook, if the vnode is a child component
-      // it should've created a child instance and mounted it. the child
-      // component also has set the placeholder vnode's elm.
-      // in that case we can just return the element and be done.
-      if (isDef(vnode.componentInstance)) {
-        initComponent(vnode, insertedVnodeQueue)
-        insert(parentElm, vnode.elm, refElm)
-        if (isTrue(isReactivated)) {
-          reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
-        }
-        return true
-      }
-    }
-  }
-
-  function initComponent(vnode, insertedVnodeQueue) {
-    if (isDef(vnode.data.pendingInsert)) {
-      insertedVnodeQueue.push.apply(
-        insertedVnodeQueue,
-        vnode.data.pendingInsert
-      )
-      vnode.data.pendingInsert = null
-    }
-    vnode.elm = vnode.componentInstance.$el
-    if (isPatchable(vnode)) {
-      invokeCreateHooks(vnode, insertedVnodeQueue)
-      setScope(vnode)
-    } else {
-      // empty component root.
-      // skip all element-related modules except for ref (#3455)
-      registerRef(vnode)
-      // make sure to invoke the insert hook
-      insertedVnodeQueue.push(vnode)
-    }
-  }
-
-  function reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm) {
-    let i
-    // hack for #4339: a reactivated component with inner transition
-    // does not trigger because the inner node's created hooks are not called
-    // again. It's not ideal to involve module-specific logic in here but
-    // there doesn't seem to be a better way to do it.
-    let innerNode = vnode
-    while (innerNode.componentInstance) {
-      innerNode = innerNode.componentInstance._vnode
-      if (isDef((i = innerNode.data)) && isDef((i = i.transition))) {
-        for (i = 0; i < cbs.activate.length; ++i) {
-          cbs.activate[i](emptyNode, innerNode)
-        }
-        insertedVnodeQueue.push(innerNode)
-        break
-      }
-    }
-    // unlike a newly created component,
-    // a reactivated keep-alive component doesn't insert itself
-    insert(parentElm, vnode.elm, refElm)
-  }
-
-  function insert(parent, elm, ref) {
-    if (isDef(parent)) {
-      if (isDef(ref)) {
-        if (nodeOps.parentNode(ref) === parent) {
-          nodeOps.insertBefore(parent, elm, ref)
-        }
-      } else {
-        nodeOps.appendChild(parent, elm)
-      }
-    }
-  }
-
-  function createChildren(vnode, children, insertedVnodeQueue) {
-    if (isArray(children)) {
-      if (__DEV__) {
-        checkDuplicateKeys(children)
-      }
-      for (let i = 0; i < children.length; ++i) {
-        createElm(
-          children[i],
-          insertedVnodeQueue,
-          vnode.elm,
-          null,
-          true,
-          children,
-          i
-        )
-      }
-    } else if (isPrimitive(vnode.text)) {
-      nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text)))
-    }
-  }
-
-  function isPatchable(vnode) {
-    while (vnode.componentInstance) {
-      vnode = vnode.componentInstance._vnode
-    }
-    return isDef(vnode.tag)
-  }
-
-  function invokeCreateHooks(vnode, insertedVnodeQueue) {
-    for (let i = 0; i < cbs.create.length; ++i) {
-      cbs.create[i](emptyNode, vnode)
-    }
-    i = vnode.data.hook // Reuse variable
-    if (isDef(i)) {
-      if (isDef(i.create)) i.create(emptyNode, vnode)
-      if (isDef(i.insert)) insertedVnodeQueue.push(vnode)
-    }
-  }
-
-  // set scope id attribute for scoped CSS.
-  // this is implemented as a special case to avoid the overhead
-  // of going through the normal attribute patching process.
-  function setScope(vnode) {
-    let i
-    if (isDef((i = vnode.fnScopeId))) {
-      nodeOps.setStyleScope(vnode.elm, i)
-    } else {
-      let ancestor = vnode
-      while (ancestor) {
-        if (isDef((i = ancestor.context)) && isDef((i = i.$options._scopeId))) {
-          nodeOps.setStyleScope(vnode.elm, i)
-        }
-        ancestor = ancestor.parent
-      }
-    }
-    // for slot content they should also get the scopeId from the host instance.
-    if (
-      isDef((i = activeInstance)) &&
-      i !== vnode.context &&
-      i !== vnode.fnContext &&
-      isDef((i = i.$options._scopeId))
-    ) {
-      nodeOps.setStyleScope(vnode.elm, i)
-    }
-  }
-
-  function addVnodes(
-    parentElm,
-    refElm,
-    vnodes,
-    startIdx,
-    endIdx,
-    insertedVnodeQueue
-  ) {
-    for (; startIdx <= endIdx; ++startIdx) {
-      createElm(
-        vnodes[startIdx],
-        insertedVnodeQueue,
-        parentElm,
-        refElm,
-        false,
-        vnodes,
-        startIdx
-      )
-    }
-  }
-
-  function invokeDestroyHook(vnode) {
-    let i, j
-    const data = vnode.data
-    if (isDef(data)) {
-      if (isDef((i = data.hook)) && isDef((i = i.destroy))) i(vnode)
-      for (i = 0; i < cbs.destroy.length; ++i) cbs.destroy[i](vnode)
-    }
-    if (isDef((i = vnode.children))) {
-      for (j = 0; j < vnode.children.length; ++j) {
-        invokeDestroyHook(vnode.children[j])
-      }
-    }
-  }
-
-  function removeVnodes(vnodes, startIdx, endIdx) {
-    for (; startIdx <= endIdx; ++startIdx) {
-      const ch = vnodes[startIdx]
-      if (isDef(ch)) {
-        if (isDef(ch.tag)) {
-          removeAndInvokeRemoveHook(ch)
-          invokeDestroyHook(ch)
-        } else {
-          // Text node
-          removeNode(ch.elm)
-        }
-      }
-    }
-  }
-
-  function removeAndInvokeRemoveHook(vnode, rm?: any) {
-    if (isDef(rm) || isDef(vnode.data)) {
-      let i
-      const listeners = cbs.remove.length + 1
-      if (isDef(rm)) {
-        // we have a recursively passed down rm callback
-        // increase the listeners count
-        rm.listeners += listeners
-      } else {
-        // directly removing
-        rm = createRmCb(vnode.elm, listeners)
-      }
-      // recursively invoke hooks on child component root node
-      if (
-        isDef((i = vnode.componentInstance)) &&
-        isDef((i = i._vnode)) &&
-        isDef(i.data)
-      ) {
-        removeAndInvokeRemoveHook(i, rm)
-      }
-      for (i = 0; i < cbs.remove.length; ++i) {
-        cbs.remove[i](vnode, rm)
-      }
-      if (isDef((i = vnode.data.hook)) && isDef((i = i.remove))) {
-        i(vnode, rm)
-      } else {
-        rm()
-      }
-    } else {
-      removeNode(vnode.elm)
-    }
-  }
-
-  function updateChildren(
-    parentElm,
-    oldCh,
-    newCh,
-    insertedVnodeQueue,
-    removeOnly
-  ) {
-    let oldStartIdx = 0
-    let newStartIdx = 0
-    let oldEndIdx = oldCh.length - 1
-    let oldStartVnode = oldCh[0]
-    let oldEndVnode = oldCh[oldEndIdx]
-    let newEndIdx = newCh.length - 1
-    let newStartVnode = newCh[0]
-    let newEndVnode = newCh[newEndIdx]
-    let oldKeyToIdx, idxInOld, vnodeToMove, refElm
-
-    // removeOnly is a special flag used only by <transition-group>
-    // to ensure removed elements stay in correct relative positions
-    // during leaving transitions
-    const canMove = !removeOnly
-
-    if (__DEV__) {
-      checkDuplicateKeys(newCh)
-    }
-
-    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
-      if (isUndef(oldStartVnode)) {
-        oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
-      } else if (isUndef(oldEndVnode)) {
-        oldEndVnode = oldCh[--oldEndIdx]
-      } else if (sameVnode(oldStartVnode, newStartVnode)) {
-        patchVnode(
-          oldStartVnode,
-          newStartVnode,
-          insertedVnodeQueue,
-          newCh,
-          newStartIdx
-        )
-        oldStartVnode = oldCh[++oldStartIdx]
-        newStartVnode = newCh[++newStartIdx]
-      } else if (sameVnode(oldEndVnode, newEndVnode)) {
-        patchVnode(
-          oldEndVnode,
-          newEndVnode,
-          insertedVnodeQueue,
-          newCh,
-          newEndIdx
-        )
-        oldEndVnode = oldCh[--oldEndIdx]
-        newEndVnode = newCh[--newEndIdx]
-      } else if (sameVnode(oldStartVnode, newEndVnode)) {
-        // Vnode moved right
-        patchVnode(
-          oldStartVnode,
-          newEndVnode,
-          insertedVnodeQueue,
-          newCh,
-          newEndIdx
-        )
-        canMove &&
-          nodeOps.insertBefore(
-            parentElm,
-            oldStartVnode.elm,
-            nodeOps.nextSibling(oldEndVnode.elm)
-          )
-        oldStartVnode = oldCh[++oldStartIdx]
-        newEndVnode = newCh[--newEndIdx]
-      } else if (sameVnode(oldEndVnode, newStartVnode)) {
-        // Vnode moved left
-        patchVnode(
-          oldEndVnode,
-          newStartVnode,
-          insertedVnodeQueue,
-          newCh,
-          newStartIdx
-        )
-        canMove &&
-          nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
-        oldEndVnode = oldCh[--oldEndIdx]
-        newStartVnode = newCh[++newStartIdx]
-      } else {
-        if (isUndef(oldKeyToIdx))
-          oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
-        idxInOld = isDef(newStartVnode.key)
-          ? oldKeyToIdx[newStartVnode.key]
-          : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
-        if (isUndef(idxInOld)) {
-          // New element
-          createElm(
-            newStartVnode,
-            insertedVnodeQueue,
-            parentElm,
-            oldStartVnode.elm,
-            false,
-            newCh,
-            newStartIdx
-          )
-        } else {
-          vnodeToMove = oldCh[idxInOld]
-          if (sameVnode(vnodeToMove, newStartVnode)) {
-            patchVnode(
-              vnodeToMove,
-              newStartVnode,
-              insertedVnodeQueue,
-              newCh,
-              newStartIdx
-            )
-            oldCh[idxInOld] = undefined
-            canMove &&
-              nodeOps.insertBefore(
-                parentElm,
-                vnodeToMove.elm,
-                oldStartVnode.elm
-              )
-          } else {
-            // same key but different element. treat as new element
-            createElm(
-              newStartVnode,
-              insertedVnodeQueue,
-              parentElm,
-              oldStartVnode.elm,
-              false,
-              newCh,
-              newStartIdx
-            )
-          }
-        }
-        newStartVnode = newCh[++newStartIdx]
-      }
-    }
-    if (oldStartIdx > oldEndIdx) {
-      refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
-      addVnodes(
-        parentElm,
-        refElm,
-        newCh,
-        newStartIdx,
-        newEndIdx,
-        insertedVnodeQueue
-      )
-    } else if (newStartIdx > newEndIdx) {
-      removeVnodes(oldCh, oldStartIdx, oldEndIdx)
-    }
-  }
-
-  function checkDuplicateKeys(children) {
-    const seenKeys = {}
-    for (let i = 0; i < children.length; i++) {
-      const vnode = children[i]
-      const key = vnode.key
-      if (isDef(key)) {
-        if (seenKeys[key]) {
-          warn(
-            `Duplicate keys detected: '${key}'. This may cause an update error.`,
-            vnode.context
-          )
-        } else {
-          seenKeys[key] = true
-        }
-      }
-    }
-  }
-
-  function findIdxInOld(node, oldCh, start, end) {
-    for (let i = start; i < end; i++) {
-      const c = oldCh[i]
-      if (isDef(c) && sameVnode(node, c)) return i
-    }
-  }
-
-  function patchVnode(
-    oldVnode,
-    vnode,
-    insertedVnodeQueue,
-    ownerArray,
-    index,
-    removeOnly?: any
-  ) {
-    if (oldVnode === vnode) {
-      return
-    }
-
-    if (isDef(vnode.elm) && isDef(ownerArray)) {
-      // clone reused vnode
-      vnode = ownerArray[index] = cloneVNode(vnode)
-    }
-
-    const elm = (vnode.elm = oldVnode.elm)
-
-    if (isTrue(oldVnode.isAsyncPlaceholder)) {
-      if (isDef(vnode.asyncFactory.resolved)) {
-        hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
-      } else {
-        vnode.isAsyncPlaceholder = true
-      }
-      return
-    }
-
-    // reuse element for static trees.
-    // note we only do this if the vnode is cloned -
-    // if the new node is not cloned it means the render functions have been
-    // reset by the hot-reload-api and we need to do a proper re-render.
-    if (
-      isTrue(vnode.isStatic) &&
-      isTrue(oldVnode.isStatic) &&
-      vnode.key === oldVnode.key &&
-      (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
-    ) {
-      vnode.componentInstance = oldVnode.componentInstance
-      return
-    }
-
-    let i
-    const data = vnode.data
-    if (isDef(data) && isDef((i = data.hook)) && isDef((i = i.prepatch))) {
-      i(oldVnode, vnode)
-    }
-
-    const oldCh = oldVnode.children
-    const ch = vnode.children
-    if (isDef(data) && isPatchable(vnode)) {
-      for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
-      if (isDef((i = data.hook)) && isDef((i = i.update))) i(oldVnode, vnode)
-    }
-    if (isUndef(vnode.text)) {
-      if (isDef(oldCh) && isDef(ch)) {
-        if (oldCh !== ch)
-          updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
-      } else if (isDef(ch)) {
-        if (__DEV__) {
-          checkDuplicateKeys(ch)
-        }
-        if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
-        addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
-      } else if (isDef(oldCh)) {
-        removeVnodes(oldCh, 0, oldCh.length - 1)
-      } else if (isDef(oldVnode.text)) {
-        nodeOps.setTextContent(elm, '')
-      }
-    } else if (oldVnode.text !== vnode.text) {
-      nodeOps.setTextContent(elm, vnode.text)
-    }
-    if (isDef(data)) {
-      if (isDef((i = data.hook)) && isDef((i = i.postpatch))) i(oldVnode, vnode)
-    }
-  }
-
-  function invokeInsertHook(vnode, queue, initial) {
-    // delay insert hooks for component root nodes, invoke them after the
-    // element is really inserted
-    if (isTrue(initial) && isDef(vnode.parent)) {
-      vnode.parent.data.pendingInsert = queue
-    } else {
-      for (let i = 0; i < queue.length; ++i) {
-        queue[i].data.hook.insert(queue[i])
-      }
-    }
-  }
-
-  let hydrationBailed = false
-  // list of modules that can skip create hook during hydration because they
-  // are already rendered on the client or has no need for initialization
-  // Note: style is excluded because it relies on initial clone for future
-  // deep updates (#7063).
-  const isRenderedModule = makeMap('attrs,class,staticClass,staticStyle,key')
-
-  // Note: this is a browser-only function so we can assume elms are DOM nodes.
-  function hydrate(elm, vnode, insertedVnodeQueue, inVPre?: boolean) {
-    let i
-    const { tag, data, children } = vnode
-    inVPre = inVPre || (data && data.pre)
-    vnode.elm = elm
-
-    if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {
-      vnode.isAsyncPlaceholder = true
-      return true
-    }
-    // assert node match
-    if (__DEV__) {
-      if (!assertNodeMatch(elm, vnode, inVPre)) {
-        return false
-      }
-    }
-    if (isDef(data)) {
-      if (isDef((i = data.hook)) && isDef((i = i.init)))
-        i(vnode, true /* hydrating */)
-      if (isDef((i = vnode.componentInstance))) {
-        // child component. it should have hydrated its own tree.
-        initComponent(vnode, insertedVnodeQueue)
-        return true
-      }
-    }
-    if (isDef(tag)) {
-      if (isDef(children)) {
-        // empty element, allow client to pick up and populate children
-        if (!elm.hasChildNodes()) {
-          createChildren(vnode, children, insertedVnodeQueue)
-        } else {
-          // v-html and domProps: innerHTML
-          if (
-            isDef((i = data)) &&
-            isDef((i = i.domProps)) &&
-            isDef((i = i.innerHTML))
-          ) {
-            if (i !== elm.innerHTML) {
-              /* istanbul ignore if */
-              if (
-                __DEV__ &&
-                typeof console !== 'undefined' &&
-                !hydrationBailed
-              ) {
-                hydrationBailed = true
-                console.warn('Parent: ', elm)
-                console.warn('server innerHTML: ', i)
-                console.warn('client innerHTML: ', elm.innerHTML)
-              }
-              return false
-            }
-          } else {
-            // iterate and compare children lists
-            let childrenMatch = true
-            let childNode = elm.firstChild
-            for (let i = 0; i < children.length; i++) {
-              if (
-                !childNode ||
-                !hydrate(childNode, children[i], insertedVnodeQueue, inVPre)
-              ) {
-                childrenMatch = false
-                break
-              }
-              childNode = childNode.nextSibling
-            }
-            // if childNode is not null, it means the actual childNodes list is
-            // longer than the virtual children list.
-            if (!childrenMatch || childNode) {
-              /* istanbul ignore if */
-              if (
-                __DEV__ &&
-                typeof console !== 'undefined' &&
-                !hydrationBailed
-              ) {
-                hydrationBailed = true
-                console.warn('Parent: ', elm)
-                console.warn(
-                  'Mismatching childNodes vs. VNodes: ',
-                  elm.childNodes,
-                  children
-                )
-              }
-              return false
-            }
-          }
-        }
-      }
-      if (isDef(data)) {
-        let fullInvoke = false
-        for (const key in data) {
-          if (!isRenderedModule(key)) {
-            fullInvoke = true
-            invokeCreateHooks(vnode, insertedVnodeQueue)
-            break
-          }
-        }
-        if (!fullInvoke && data['class']) {
-          // ensure collecting deps for deep class bindings for future updates
-          traverse(data['class'])
-        }
-      }
-    } else if (elm.data !== vnode.text) {
-      elm.data = vnode.text
-    }
-    return true
-  }
-
-  function assertNodeMatch(node, vnode, inVPre) {
-    if (isDef(vnode.tag)) {
-      return (
-        vnode.tag.indexOf('vue-component') === 0 ||
-        (!isUnknownElement(vnode, inVPre) &&
-          vnode.tag.toLowerCase() ===
-            (node.tagName && node.tagName.toLowerCase()))
-      )
-    } else {
-      return node.nodeType === (vnode.isComment ? 8 : 3)
-    }
-  }
-
-  return function patch(oldVnode, vnode, hydrating, removeOnly) {
-    if (isUndef(vnode)) {
-      if (isDef(oldVnode)) invokeDestroyHook(oldVnode)
-      return
-    }
-
-    let isInitialPatch = false
-    const insertedVnodeQueue: any[] = []
-
-    if (isUndef(oldVnode)) {
-      // empty mount (likely as component), create new root element
-      isInitialPatch = true
-      createElm(vnode, insertedVnodeQueue)
-    } else {
-      const isRealElement = isDef(oldVnode.nodeType)
-      if (!isRealElement && sameVnode(oldVnode, vnode)) {
-        // patch existing root node
-        patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
-      } else {
-        if (isRealElement) {
-          // mounting to a real element
-          // check if this is server-rendered content and if we can perform
-          // a successful hydration.
-          if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
-            oldVnode.removeAttribute(SSR_ATTR)
-            hydrating = true
-          }
-          if (isTrue(hydrating)) {
-            if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {
-              invokeInsertHook(vnode, insertedVnodeQueue, true)
-              return oldVnode
-            } else if (__DEV__) {
-              warn(
-                'The client-side rendered virtual DOM tree is not matching ' +
-                  'server-rendered content. This is likely caused by incorrect ' +
-                  'HTML markup, for example nesting block-level elements inside ' +
-                  '<p>, or missing <tbody>. Bailing hydration and performing ' +
-                  'full client-side render.'
-              )
-            }
-          }
-          // either not server-rendered, or hydration failed.
-          // create an empty node and replace it
-          oldVnode = emptyNodeAt(oldVnode)
-        }
-
-        // replacing existing element
-        const oldElm = oldVnode.elm
-        const parentElm = nodeOps.parentNode(oldElm)
-
-        // create new node
-        createElm(
-          vnode,
-          insertedVnodeQueue,
-          // extremely rare edge case: do not insert if old element is in a
-          // leaving transition. Only happens when combining transition +
-          // keep-alive + HOCs. (#4590)
-          oldElm._leaveCb ? null : parentElm,
-          nodeOps.nextSibling(oldElm)
-        )
-
-        // update parent placeholder node element, recursively
-        if (isDef(vnode.parent)) {
-          let ancestor = vnode.parent
-          const patchable = isPatchable(vnode)
-          while (ancestor) {
-            for (let i = 0; i < cbs.destroy.length; ++i) {
-              cbs.destroy[i](ancestor)
-            }
-            ancestor.elm = vnode.elm
-            if (patchable) {
-              for (let i = 0; i < cbs.create.length; ++i) {
-                cbs.create[i](emptyNode, ancestor)
-              }
-              // #6513
-              // invoke insert hooks that may have been merged by create hooks.
-              // e.g. for directives that uses the "inserted" hook.
-              const insert = ancestor.data.hook.insert
-              if (insert.merged) {
-                // start at index 1 to avoid re-invoking component mounted hook
-                // clone insert hooks to avoid being mutated during iteration.
-                // e.g. for customed directives under transition group.
-                const cloned = insert.fns.slice(1)
-                for (let i = 0; i < cloned.length; i++) {
-                  cloned[i]()
-                }
-              }
-            } else {
-              registerRef(ancestor)
-            }
-            ancestor = ancestor.parent
-          }
-        }
-
-        // destroy old node
-        if (isDef(parentElm)) {
-          removeVnodes([oldVnode], 0, 0)
-        } else if (isDef(oldVnode.tag)) {
-          invokeDestroyHook(oldVnode)
-        }
-      }
-    }
-
-    invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)
-    return vnode.elm
-  }
-}
diff --git a/src/core/vdom/vnode.ts b/src/core/vdom/vnode.ts
deleted file mode 100644
index 3b57f9aca0c..00000000000
--- a/src/core/vdom/vnode.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-import type { Component } from 'types/component'
-import type { ComponentOptions } from 'types/options'
-import type { VNodeComponentOptions, VNodeData } from 'types/vnode'
-
-/**
- * @internal
- */
-export default class VNode {
-  tag?: string
-  data: VNodeData | undefined
-  children?: Array<VNode> | null
-  text?: string
-  elm: Node | undefined
-  ns?: string
-  context?: Component // rendered in this component's scope
-  key: string | number | undefined
-  componentOptions?: VNodeComponentOptions
-  componentInstance?: Component // component instance
-  parent: VNode | undefined | null // component placeholder node
-
-  // strictly internal
-  raw: boolean // contains raw HTML? (server only)
-  isStatic: boolean // hoisted static node
-  isRootInsert: boolean // necessary for enter transition check
-  isComment: boolean // empty comment placeholder?
-  isCloned: boolean // is a cloned node?
-  isOnce: boolean // is a v-once node?
-  asyncFactory?: Function // async component factory function
-  asyncMeta: Object | void
-  isAsyncPlaceholder: boolean
-  ssrContext?: Object | void
-  fnContext: Component | void // real context vm for functional nodes
-  fnOptions?: ComponentOptions | null // for SSR caching
-  devtoolsMeta?: Object | null // used to store functional render context for devtools
-  fnScopeId?: string | null // functional scope id support
-  isComponentRootElement?: boolean | null // for SSR directives
-
-  constructor(
-    tag?: string,
-    data?: VNodeData,
-    children?: Array<VNode> | null,
-    text?: string,
-    elm?: Node,
-    context?: Component,
-    componentOptions?: VNodeComponentOptions,
-    asyncFactory?: Function
-  ) {
-    this.tag = tag
-    this.data = data
-    this.children = children
-    this.text = text
-    this.elm = elm
-    this.ns = undefined
-    this.context = context
-    this.fnContext = undefined
-    this.fnOptions = undefined
-    this.fnScopeId = undefined
-    this.key = data && data.key
-    this.componentOptions = componentOptions
-    this.componentInstance = undefined
-    this.parent = undefined
-    this.raw = false
-    this.isStatic = false
-    this.isRootInsert = true
-    this.isComment = false
-    this.isCloned = false
-    this.isOnce = false
-    this.asyncFactory = asyncFactory
-    this.asyncMeta = undefined
-    this.isAsyncPlaceholder = false
-  }
-
-  // DEPRECATED: alias for componentInstance for backwards compat.
-  /* istanbul ignore next */
-  get child(): Component | void {
-    return this.componentInstance
-  }
-}
-
-export const createEmptyVNode = (text: string = '') => {
-  const node = new VNode()
-  node.text = text
-  node.isComment = true
-  return node
-}
-
-export function createTextVNode(val: string | number) {
-  return new VNode(undefined, undefined, undefined, String(val))
-}
-
-// optimized shallow clone
-// used for static nodes and slot nodes because they may be reused across
-// multiple renders, cloning them avoids errors when DOM manipulations rely
-// on their elm reference.
-export function cloneVNode(vnode: VNode): VNode {
-  const cloned = new VNode(
-    vnode.tag,
-    vnode.data,
-    // #7975
-    // clone children array to avoid mutating original in case of cloning
-    // a child.
-    vnode.children && vnode.children.slice(),
-    vnode.text,
-    vnode.elm,
-    vnode.context,
-    vnode.componentOptions,
-    vnode.asyncFactory
-  )
-  cloned.ns = vnode.ns
-  cloned.isStatic = vnode.isStatic
-  cloned.key = vnode.key
-  cloned.isComment = vnode.isComment
-  cloned.fnContext = vnode.fnContext
-  cloned.fnOptions = vnode.fnOptions
-  cloned.fnScopeId = vnode.fnScopeId
-  cloned.asyncMeta = vnode.asyncMeta
-  cloned.isCloned = true
-  return cloned
-}
diff --git a/src/directive.js b/src/directive.js
new file mode 100644
index 00000000000..5140fa3ab88
--- /dev/null
+++ b/src/directive.js
@@ -0,0 +1,335 @@
+import {
+  extend,
+  bind,
+  on,
+  off,
+  getAttr,
+  getBindAttr,
+  camelize,
+  hyphenate,
+  nextTick,
+  warn
+} from './util/index'
+import Watcher from './watcher'
+import { parseExpression, isSimplePath } from './parsers/expression'
+
+function noop () {}
+
+/**
+ * A directive links a DOM element with a piece of data,
+ * which is the result of evaluating an expression.
+ * It registers a watcher with the expression and calls
+ * the DOM update function when a change is triggered.
+ *
+ * @param {Object} descriptor
+ *                 - {String} name
+ *                 - {Object} def
+ *                 - {String} expression
+ *                 - {Array<Object>} [filters]
+ *                 - {Object} [modifiers]
+ *                 - {Boolean} literal
+ *                 - {String} attr
+ *                 - {String} arg
+ *                 - {String} raw
+ *                 - {String} [ref]
+ *                 - {Array<Object>} [interp]
+ *                 - {Boolean} [hasOneTime]
+ * @param {Vue} vm
+ * @param {Node} el
+ * @param {Vue} [host] - transclusion host component
+ * @param {Object} [scope] - v-for scope
+ * @param {Fragment} [frag] - owner fragment
+ * @constructor
+ */
+
+export default function Directive (descriptor, vm, el, host, scope, frag) {
+  this.vm = vm
+  this.el = el
+  // copy descriptor properties
+  this.descriptor = descriptor
+  this.name = descriptor.name
+  this.expression = descriptor.expression
+  this.arg = descriptor.arg
+  this.modifiers = descriptor.modifiers
+  this.filters = descriptor.filters
+  this.literal = this.modifiers && this.modifiers.literal
+  // private
+  this._locked = false
+  this._bound = false
+  this._listeners = null
+  // link context
+  this._host = host
+  this._scope = scope
+  this._frag = frag
+  // store directives on node in dev mode
+  if (process.env.NODE_ENV !== 'production' && this.el) {
+    this.el._vue_directives = this.el._vue_directives || []
+    this.el._vue_directives.push(this)
+  }
+}
+
+/**
+ * Initialize the directive, mixin definition properties,
+ * setup the watcher, call definition bind() and update()
+ * if present.
+ */
+
+Directive.prototype._bind = function () {
+  var name = this.name
+  var descriptor = this.descriptor
+
+  // remove attribute
+  if (
+    (name !== 'cloak' || this.vm._isCompiled) &&
+    this.el && this.el.removeAttribute
+  ) {
+    var attr = descriptor.attr || ('v-' + name)
+    this.el.removeAttribute(attr)
+  }
+
+  // copy def properties
+  var def = descriptor.def
+  if (typeof def === 'function') {
+    this.update = def
+  } else {
+    extend(this, def)
+  }
+
+  // setup directive params
+  this._setupParams()
+
+  // initial bind
+  if (this.bind) {
+    this.bind()
+  }
+  this._bound = true
+
+  if (this.literal) {
+    this.update && this.update(descriptor.raw)
+  } else if (
+    (this.expression || this.modifiers) &&
+    (this.update || this.twoWay) &&
+    !this._checkStatement()
+  ) {
+    // wrapped updater for context
+    var dir = this
+    if (this.update) {
+      this._update = function (val, oldVal) {
+        if (!dir._locked) {
+          dir.update(val, oldVal)
+        }
+      }
+    } else {
+      this._update = noop
+    }
+    var preProcess = this._preProcess
+      ? bind(this._preProcess, this)
+      : null
+    var postProcess = this._postProcess
+      ? bind(this._postProcess, this)
+      : null
+    var watcher = this._watcher = new Watcher(
+      this.vm,
+      this.expression,
+      this._update, // callback
+      {
+        filters: this.filters,
+        twoWay: this.twoWay,
+        deep: this.deep,
+        preProcess: preProcess,
+        postProcess: postProcess,
+        scope: this._scope
+      }
+    )
+    // v-model with inital inline value need to sync back to
+    // model instead of update to DOM on init. They would
+    // set the afterBind hook to indicate that.
+    if (this.afterBind) {
+      this.afterBind()
+    } else if (this.update) {
+      this.update(watcher.value)
+    }
+  }
+}
+
+/**
+ * Setup all param attributes, e.g. track-by,
+ * transition-mode, etc...
+ */
+
+Directive.prototype._setupParams = function () {
+  if (!this.params) {
+    return
+  }
+  var params = this.params
+  // swap the params array with a fresh object.
+  this.params = Object.create(null)
+  var i = params.length
+  var key, val, mappedKey
+  while (i--) {
+    key = hyphenate(params[i])
+    mappedKey = camelize(key)
+    val = getBindAttr(this.el, key)
+    if (val != null) {
+      // dynamic
+      this._setupParamWatcher(mappedKey, val)
+    } else {
+      // static
+      val = getAttr(this.el, key)
+      if (val != null) {
+        this.params[mappedKey] = val === '' ? true : val
+      }
+    }
+  }
+}
+
+/**
+ * Setup a watcher for a dynamic param.
+ *
+ * @param {String} key
+ * @param {String} expression
+ */
+
+Directive.prototype._setupParamWatcher = function (key, expression) {
+  var self = this
+  var called = false
+  var unwatch = (this._scope || this.vm).$watch(expression, function (val, oldVal) {
+    self.params[key] = val
+    // since we are in immediate mode,
+    // only call the param change callbacks if this is not the first update.
+    if (called) {
+      var cb = self.paramWatchers && self.paramWatchers[key]
+      if (cb) {
+        cb.call(self, val, oldVal)
+      }
+    } else {
+      called = true
+    }
+  }, {
+    immediate: true,
+    user: false
+  })
+  ;(this._paramUnwatchFns || (this._paramUnwatchFns = [])).push(unwatch)
+}
+
+/**
+ * Check if the directive is a function caller
+ * and if the expression is a callable one. If both true,
+ * we wrap up the expression and use it as the event
+ * handler.
+ *
+ * e.g. on-click="a++"
+ *
+ * @return {Boolean}
+ */
+
+Directive.prototype._checkStatement = function () {
+  var expression = this.expression
+  if (
+    expression && this.acceptStatement &&
+    !isSimplePath(expression)
+  ) {
+    var fn = parseExpression(expression).get
+    var scope = this._scope || this.vm
+    var handler = function (e) {
+      scope.$event = e
+      fn.call(scope, scope)
+      scope.$event = null
+    }
+    if (this.filters) {
+      handler = scope._applyFilters(handler, null, this.filters)
+    }
+    this.update(handler)
+    return true
+  }
+}
+
+/**
+ * Set the corresponding value with the setter.
+ * This should only be used in two-way directives
+ * e.g. v-model.
+ *
+ * @param {*} value
+ * @public
+ */
+
+Directive.prototype.set = function (value) {
+  /* istanbul ignore else */
+  if (this.twoWay) {
+    this._withLock(function () {
+      this._watcher.set(value)
+    })
+  } else if (process.env.NODE_ENV !== 'production') {
+    warn(
+      'Directive.set() can only be used inside twoWay' +
+      'directives.'
+    )
+  }
+}
+
+/**
+ * Execute a function while preventing that function from
+ * triggering updates on this directive instance.
+ *
+ * @param {Function} fn
+ */
+
+Directive.prototype._withLock = function (fn) {
+  var self = this
+  self._locked = true
+  fn.call(self)
+  nextTick(function () {
+    self._locked = false
+  })
+}
+
+/**
+ * Convenience method that attaches a DOM event listener
+ * to the directive element and autometically tears it down
+ * during unbind.
+ *
+ * @param {String} event
+ * @param {Function} handler
+ * @param {Boolean} [useCapture]
+ */
+
+Directive.prototype.on = function (event, handler, useCapture) {
+  on(this.el, event, handler, useCapture)
+  ;(this._listeners || (this._listeners = []))
+    .push([event, handler])
+}
+
+/**
+ * Teardown the watcher and call unbind.
+ */
+
+Directive.prototype._teardown = function () {
+  if (this._bound) {
+    this._bound = false
+    if (this.unbind) {
+      this.unbind()
+    }
+    if (this._watcher) {
+      this._watcher.teardown()
+    }
+    var listeners = this._listeners
+    var i
+    if (listeners) {
+      i = listeners.length
+      while (i--) {
+        off(this.el, listeners[i][0], listeners[i][1])
+      }
+    }
+    var unwatchFns = this._paramUnwatchFns
+    if (unwatchFns) {
+      i = unwatchFns.length
+      while (i--) {
+        unwatchFns[i]()
+      }
+    }
+    if (process.env.NODE_ENV !== 'production' && this.el) {
+      this.el._vue_directives.$remove(this)
+    }
+    this.vm = this.el = this._watcher = this._listeners = null
+  }
+}
diff --git a/src/directives/element/index.js b/src/directives/element/index.js
new file mode 100644
index 00000000000..c3f1e85a8b4
--- /dev/null
+++ b/src/directives/element/index.js
@@ -0,0 +1,7 @@
+import slot from './slot'
+import partial from './partial'
+
+export default {
+  slot,
+  partial
+}
diff --git a/src/directives/element/partial.js b/src/directives/element/partial.js
new file mode 100644
index 00000000000..d061e571ccf
--- /dev/null
+++ b/src/directives/element/partial.js
@@ -0,0 +1,45 @@
+import vIf from '../public/if'
+import FragmentFactory from '../../fragment/factory'
+import { PARTIAL } from '../priorities'
+import {
+  createAnchor,
+  replace,
+  resolveAsset
+} from '../../util/index'
+
+export default {
+
+  priority: PARTIAL,
+
+  params: ['name'],
+
+  // watch changes to name for dynamic partials
+  paramWatchers: {
+    name (value) {
+      vIf.remove.call(this)
+      if (value) {
+        this.insert(value)
+      }
+    }
+  },
+
+  bind () {
+    this.anchor = createAnchor('v-partial')
+    replace(this.el, this.anchor)
+    this.insert(this.params.name)
+  },
+
+  insert (id) {
+    var partial = resolveAsset(this.vm.$options, 'partials', id, true)
+    if (partial) {
+      this.factory = new FragmentFactory(this.vm, partial)
+      vIf.insert.call(this)
+    }
+  },
+
+  unbind () {
+    if (this.frag) {
+      this.frag.destroy()
+    }
+  }
+}
diff --git a/src/directives/element/slot.js b/src/directives/element/slot.js
new file mode 100644
index 00000000000..cb9aa6d8394
--- /dev/null
+++ b/src/directives/element/slot.js
@@ -0,0 +1,64 @@
+import { SLOT } from '../priorities'
+import {
+  extractContent,
+  replace,
+  remove
+} from '../../util/index'
+
+export default {
+
+  priority: SLOT,
+  params: ['name'],
+
+  bind () {
+    // this was resolved during component transclusion
+    var name = this.params.name || 'default'
+    var content = this.vm._slotContents && this.vm._slotContents[name]
+    if (!content || !content.hasChildNodes()) {
+      this.fallback()
+    } else {
+      this.compile(content.cloneNode(true), this.vm._context, this.vm)
+    }
+  },
+
+  compile (content, context, host) {
+    if (content && context) {
+      if (
+        this.el.hasChildNodes() &&
+        content.childNodes.length === 1 &&
+        content.childNodes[0].nodeType === 1 &&
+        content.childNodes[0].hasAttribute('v-if')
+      ) {
+        // if the inserted slot has v-if
+        // inject fallback content as the v-else
+        const elseBlock = document.createElement('template')
+        elseBlock.setAttribute('v-else', '')
+        elseBlock.innerHTML = this.el.innerHTML
+        // the else block should be compiled in child scope
+        elseBlock._context = this.vm
+        content.appendChild(elseBlock)
+      }
+      const scope = host
+        ? host._scope
+        : this._scope
+      this.unlink = context.$compile(
+        content, host, scope, this._frag
+      )
+    }
+    if (content) {
+      replace(this.el, content)
+    } else {
+      remove(this.el)
+    }
+  },
+
+  fallback () {
+    this.compile(extractContent(this.el, true), this.vm)
+  },
+
+  unbind () {
+    if (this.unlink) {
+      this.unlink()
+    }
+  }
+}
diff --git a/src/directives/internal/class.js b/src/directives/internal/class.js
new file mode 100644
index 00000000000..e4c1ce50226
--- /dev/null
+++ b/src/directives/internal/class.js
@@ -0,0 +1,101 @@
+import {
+  addClass,
+  removeClass,
+  isArray,
+  isObject
+} from '../../util/index'
+
+export default {
+
+  deep: true,
+
+  update (value) {
+    if (!value) {
+      this.cleanup()
+    } else if (typeof value === 'string') {
+      this.setClass(value.trim().split(/\s+/))
+    } else {
+      this.setClass(normalize(value))
+    }
+  },
+
+  setClass (value) {
+    this.cleanup(value)
+    for (var i = 0, l = value.length; i < l; i++) {
+      var val = value[i]
+      if (val) {
+        apply(this.el, val, addClass)
+      }
+    }
+    this.prevKeys = value
+  },
+
+  cleanup (value) {
+    const prevKeys = this.prevKeys
+    if (!prevKeys) return
+    var i = prevKeys.length
+    while (i--) {
+      var key = prevKeys[i]
+      if (!value || value.indexOf(key) < 0) {
+        apply(this.el, key, removeClass)
+      }
+    }
+  }
+}
+
+/**
+ * Normalize objects and arrays (potentially containing objects)
+ * into array of strings.
+ *
+ * @param {Object|Array<String|Object>} value
+ * @return {Array<String>}
+ */
+
+function normalize (value) {
+  const res = []
+  if (isArray(value)) {
+    for (var i = 0, l = value.length; i < l; i++) {
+      const key = value[i]
+      if (key) {
+        if (typeof key === 'string') {
+          res.push(key)
+        } else {
+          for (var k in key) {
+            if (key[k]) res.push(k)
+          }
+        }
+      }
+    }
+  } else if (isObject(value)) {
+    for (var key in value) {
+      if (value[key]) res.push(key)
+    }
+  }
+  return res
+}
+
+/**
+ * Add or remove a class/classes on an element
+ *
+ * @param {Element} el
+ * @param {String} key The class name. This may or may not
+ *                     contain a space character, in such a
+ *                     case we'll deal with multiple class
+ *                     names at once.
+ * @param {Function} fn
+ */
+
+function apply (el, key, fn) {
+  key = key.trim()
+  if (key.indexOf(' ') === -1) {
+    fn(el, key)
+    return
+  }
+  // The key contains one or more space characters.
+  // Since a class name doesn't accept such characters, we
+  // treat it as multiple classes.
+  var keys = key.split(/\s+/)
+  for (var i = 0, l = keys.length; i < l; i++) {
+    fn(el, keys[i])
+  }
+}
diff --git a/src/directives/internal/component.js b/src/directives/internal/component.js
new file mode 100644
index 00000000000..6148ef799a5
--- /dev/null
+++ b/src/directives/internal/component.js
@@ -0,0 +1,389 @@
+import { cloneNode } from '../../parsers/template'
+import { COMPONENT } from '../priorities'
+import {
+  extractContent,
+  createAnchor,
+  replace,
+  hyphenate,
+  warn,
+  cancellable,
+  extend
+} from '../../util/index'
+
+export default {
+
+  priority: COMPONENT,
+
+  params: [
+    'keep-alive',
+    'transition-mode',
+    'inline-template'
+  ],
+
+  /**
+   * Setup. Two possible usages:
+   *
+   * - static:
+   *   <comp> or <div v-component="comp">
+   *
+   * - dynamic:
+   *   <component :is="view">
+   */
+
+  bind () {
+    if (!this.el.__vue__) {
+      // keep-alive cache
+      this.keepAlive = this.params.keepAlive
+      if (this.keepAlive) {
+        this.cache = {}
+      }
+      // check inline-template
+      if (this.params.inlineTemplate) {
+        // extract inline template as a DocumentFragment
+        this.inlineTemplate = extractContent(this.el, true)
+      }
+      // component resolution related state
+      this.pendingComponentCb =
+      this.Component = null
+      // transition related state
+      this.pendingRemovals = 0
+      this.pendingRemovalCb = null
+      // create a ref anchor
+      this.anchor = createAnchor('v-component')
+      replace(this.el, this.anchor)
+      // remove is attribute.
+      // this is removed during compilation, but because compilation is
+      // cached, when the component is used elsewhere this attribute
+      // will remain at link time.
+      this.el.removeAttribute('is')
+      this.el.removeAttribute(':is')
+      // remove ref, same as above
+      if (this.descriptor.ref) {
+        this.el.removeAttribute('v-ref:' + hyphenate(this.descriptor.ref))
+      }
+      // if static, build right now.
+      if (this.literal) {
+        this.setComponent(this.expression)
+      }
+    } else {
+      process.env.NODE_ENV !== 'production' && warn(
+        'cannot mount component "' + this.expression + '" ' +
+        'on already mounted element: ' + this.el
+      )
+    }
+  },
+
+  /**
+   * Public update, called by the watcher in the dynamic
+   * literal scenario, e.g. <component :is="view">
+   */
+
+  update (value) {
+    if (!this.literal) {
+      this.setComponent(value)
+    }
+  },
+
+  /**
+   * Switch dynamic components. May resolve the component
+   * asynchronously, and perform transition based on
+   * specified transition mode. Accepts a few additional
+   * arguments specifically for vue-router.
+   *
+   * The callback is called when the full transition is
+   * finished.
+   *
+   * @param {String} value
+   * @param {Function} [cb]
+   */
+
+  setComponent (value, cb) {
+    this.invalidatePending()
+    if (!value) {
+      // just remove current
+      this.unbuild(true)
+      this.remove(this.childVM, cb)
+      this.childVM = null
+    } else {
+      var self = this
+      this.resolveComponent(value, function () {
+        self.mountComponent(cb)
+      })
+    }
+  },
+
+  /**
+   * Resolve the component constructor to use when creating
+   * the child vm.
+   *
+   * @param {String|Function} value
+   * @param {Function} cb
+   */
+
+  resolveComponent (value, cb) {
+    var self = this
+    this.pendingComponentCb = cancellable(function (Component) {
+      self.ComponentName =
+        Component.options.name ||
+        (typeof value === 'string' ? value : null)
+      self.Component = Component
+      cb()
+    })
+    this.vm._resolveComponent(value, this.pendingComponentCb)
+  },
+
+  /**
+   * Create a new instance using the current constructor and
+   * replace the existing instance. This method doesn't care
+   * whether the new component and the old one are actually
+   * the same.
+   *
+   * @param {Function} [cb]
+   */
+
+  mountComponent (cb) {
+    // actual mount
+    this.unbuild(true)
+    var self = this
+    var activateHooks = this.Component.options.activate
+    var cached = this.getCached()
+    var newComponent = this.build()
+    if (activateHooks && !cached) {
+      this.waitingFor = newComponent
+      callActivateHooks(activateHooks, newComponent, function () {
+        if (self.waitingFor !== newComponent) {
+          return
+        }
+        self.waitingFor = null
+        self.transition(newComponent, cb)
+      })
+    } else {
+      // update ref for kept-alive component
+      if (cached) {
+        newComponent._updateRef()
+      }
+      this.transition(newComponent, cb)
+    }
+  },
+
+  /**
+   * When the component changes or unbinds before an async
+   * constructor is resolved, we need to invalidate its
+   * pending callback.
+   */
+
+  invalidatePending () {
+    if (this.pendingComponentCb) {
+      this.pendingComponentCb.cancel()
+      this.pendingComponentCb = null
+    }
+  },
+
+  /**
+   * Instantiate/insert a new child vm.
+   * If keep alive and has cached instance, insert that
+   * instance; otherwise build a new one and cache it.
+   *
+   * @param {Object} [extraOptions]
+   * @return {Vue} - the created instance
+   */
+
+  build (extraOptions) {
+    var cached = this.getCached()
+    if (cached) {
+      return cached
+    }
+    if (this.Component) {
+      // default options
+      var options = {
+        name: this.ComponentName,
+        el: cloneNode(this.el),
+        template: this.inlineTemplate,
+        // make sure to add the child with correct parent
+        // if this is a transcluded component, its parent
+        // should be the transclusion host.
+        parent: this._host || this.vm,
+        // if no inline-template, then the compiled
+        // linker can be cached for better performance.
+        _linkerCachable: !this.inlineTemplate,
+        _ref: this.descriptor.ref,
+        _asComponent: true,
+        _isRouterView: this._isRouterView,
+        // if this is a transcluded component, context
+        // will be the common parent vm of this instance
+        // and its host.
+        _context: this.vm,
+        // if this is inside an inline v-for, the scope
+        // will be the intermediate scope created for this
+        // repeat fragment. this is used for linking props
+        // and container directives.
+        _scope: this._scope,
+        // pass in the owner fragment of this component.
+        // this is necessary so that the fragment can keep
+        // track of its contained components in order to
+        // call attach/detach hooks for them.
+        _frag: this._frag
+      }
+      // extra options
+      // in 1.0.0 this is used by vue-router only
+      /* istanbul ignore if */
+      if (extraOptions) {
+        extend(options, extraOptions)
+      }
+      var child = new this.Component(options)
+      if (this.keepAlive) {
+        this.cache[this.Component.cid] = child
+      }
+      /* istanbul ignore if */
+      if (process.env.NODE_ENV !== 'production' &&
+          this.el.hasAttribute('transition') &&
+          child._isFragment) {
+        warn(
+          'Transitions will not work on a fragment instance. ' +
+          'Template: ' + child.$options.template,
+          child
+        )
+      }
+      return child
+    }
+  },
+
+  /**
+   * Try to get a cached instance of the current component.
+   *
+   * @return {Vue|undefined}
+   */
+
+  getCached () {
+    return this.keepAlive && this.cache[this.Component.cid]
+  },
+
+  /**
+   * Teardown the current child, but defers cleanup so
+   * that we can separate the destroy and removal steps.
+   *
+   * @param {Boolean} defer
+   */
+
+  unbuild (defer) {
+    if (this.waitingFor) {
+      if (!this.keepAlive) {
+        this.waitingFor.$destroy()
+      }
+      this.waitingFor = null
+    }
+    var child = this.childVM
+    if (!child || this.keepAlive) {
+      if (child) {
+        // remove ref
+        child._inactive = true
+        child._updateRef(true)
+      }
+      return
+    }
+    // the sole purpose of `deferCleanup` is so that we can
+    // "deactivate" the vm right now and perform DOM removal
+    // later.
+    child.$destroy(false, defer)
+  },
+
+  /**
+   * Remove current destroyed child and manually do
+   * the cleanup after removal.
+   *
+   * @param {Function} cb
+   */
+
+  remove (child, cb) {
+    var keepAlive = this.keepAlive
+    if (child) {
+      // we may have a component switch when a previous
+      // component is still being transitioned out.
+      // we want to trigger only one lastest insertion cb
+      // when the existing transition finishes. (#1119)
+      this.pendingRemovals++
+      this.pendingRemovalCb = cb
+      var self = this
+      child.$remove(function () {
+        self.pendingRemovals--
+        if (!keepAlive) child._cleanup()
+        if (!self.pendingRemovals && self.pendingRemovalCb) {
+          self.pendingRemovalCb()
+          self.pendingRemovalCb = null
+        }
+      })
+    } else if (cb) {
+      cb()
+    }
+  },
+
+  /**
+   * Actually swap the components, depending on the
+   * transition mode. Defaults to simultaneous.
+   *
+   * @param {Vue} target
+   * @param {Function} [cb]
+   */
+
+  transition (target, cb) {
+    var self = this
+    var current = this.childVM
+    // for devtool inspection
+    if (current) current._inactive = true
+    target._inactive = false
+    this.childVM = target
+    switch (self.params.transitionMode) {
+      case 'in-out':
+        target.$before(self.anchor, function () {
+          self.remove(current, cb)
+        })
+        break
+      case 'out-in':
+        self.remove(current, function () {
+          target.$before(self.anchor, cb)
+        })
+        break
+      default:
+        self.remove(current)
+        target.$before(self.anchor, cb)
+    }
+  },
+
+  /**
+   * Unbind.
+   */
+
+  unbind () {
+    this.invalidatePending()
+    // Do not defer cleanup when unbinding
+    this.unbuild()
+    // destroy all keep-alive cached instances
+    if (this.cache) {
+      for (var key in this.cache) {
+        this.cache[key].$destroy()
+      }
+      this.cache = null
+    }
+  }
+}
+
+/**
+ * Call activate hooks in order (asynchronous)
+ *
+ * @param {Array} hooks
+ * @param {Vue} vm
+ * @param {Function} cb
+ */
+
+function callActivateHooks (hooks, vm, cb) {
+  var total = hooks.length
+  var called = 0
+  hooks[0].call(vm, next)
+  function next () {
+    if (++called >= total) {
+      cb()
+    } else {
+      hooks[called].call(vm, next)
+    }
+  }
+}
diff --git a/src/directives/internal/index.js b/src/directives/internal/index.js
new file mode 100644
index 00000000000..b46d3296129
--- /dev/null
+++ b/src/directives/internal/index.js
@@ -0,0 +1,13 @@
+import style from './style'
+import vClass from './class'
+import component from './component'
+import prop from './prop'
+import transition from './transition'
+
+export default {
+  style,
+  'class': vClass,
+  component,
+  prop,
+  transition
+}
diff --git a/src/directives/internal/prop.js b/src/directives/internal/prop.js
new file mode 100644
index 00000000000..71461fb1d8f
--- /dev/null
+++ b/src/directives/internal/prop.js
@@ -0,0 +1,68 @@
+// NOTE: the prop internal directive is compiled and linked
+// during _initProps(), before the created hook is called.
+// The purpose is to make the initial prop values available
+// inside `created` hooks and `data` functions.
+
+import Watcher from '../../watcher'
+import config from '../../config'
+import { initProp, updateProp } from '../../compiler/compile-props'
+
+const bindingModes = config._propBindingModes
+
+export default {
+
+  bind () {
+    const child = this.vm
+    const parent = child._context
+    // passed in from compiler directly
+    const prop = this.descriptor.prop
+    const childKey = prop.path
+    const parentKey = prop.parentPath
+    const twoWay = prop.mode === bindingModes.TWO_WAY
+
+    const parentWatcher = this.parentWatcher = new Watcher(
+      parent,
+      parentKey,
+      function (val) {
+        updateProp(child, prop, val)
+      }, {
+        twoWay: twoWay,
+        filters: prop.filters,
+        // important: props need to be observed on the
+        // v-for scope if present
+        scope: this._scope
+      }
+    )
+
+    // set the child initial value.
+    initProp(child, prop, parentWatcher.value)
+
+    // setup two-way binding
+    if (twoWay) {
+      // important: defer the child watcher creation until
+      // the created hook (after data observation)
+      var self = this
+      child.$once('pre-hook:created', function () {
+        self.childWatcher = new Watcher(
+          child,
+          childKey,
+          function (val) {
+            parentWatcher.set(val)
+          }, {
+            // ensure sync upward before parent sync down.
+            // this is necessary in cases e.g. the child
+            // mutates a prop array, then replaces it. (#1683)
+            sync: true
+          }
+        )
+      })
+    }
+  },
+
+  unbind () {
+    this.parentWatcher.teardown()
+    if (this.childWatcher) {
+      this.childWatcher.teardown()
+    }
+  }
+}
diff --git a/src/directives/internal/style.js b/src/directives/internal/style.js
new file mode 100644
index 00000000000..6ad49e472e3
--- /dev/null
+++ b/src/directives/internal/style.js
@@ -0,0 +1,130 @@
+import {
+  extend,
+  isArray,
+  hyphenate,
+  camelize,
+  warn
+} from '../../util/index'
+
+const prefixes = ['-webkit-', '-moz-', '-ms-']
+const camelPrefixes = ['Webkit', 'Moz', 'ms']
+const importantRE = /!important;?$/
+const propCache = Object.create(null)
+
+let testEl = null
+
+export default {
+
+  deep: true,
+
+  update (value) {
+    if (typeof value === 'string') {
+      this.el.style.cssText = value
+    } else if (isArray(value)) {
+      this.handleObject(value.reduce(extend, {}))
+    } else {
+      this.handleObject(value || {})
+    }
+  },
+
+  handleObject (value) {
+    // cache object styles so that only changed props
+    // are actually updated.
+    var cache = this.cache || (this.cache = {})
+    var name, val
+    for (name in cache) {
+      if (!(name in value)) {
+        this.handleSingle(name, null)
+        delete cache[name]
+      }
+    }
+    for (name in value) {
+      val = value[name]
+      if (val !== cache[name]) {
+        cache[name] = val
+        this.handleSingle(name, val)
+      }
+    }
+  },
+
+  handleSingle (prop, value) {
+    prop = normalize(prop)
+    if (!prop) return // unsupported prop
+    // cast possible numbers/booleans into strings
+    if (value != null) value += ''
+    if (value) {
+      var isImportant = importantRE.test(value)
+        ? 'important'
+        : ''
+      if (isImportant) {
+        /* istanbul ignore if */
+        if (process.env.NODE_ENV !== 'production') {
+          warn(
+            'It\'s probably a bad idea to use !important with inline rules. ' +
+            'This feature will be deprecated in a future version of Vue.'
+          )
+        }
+        value = value.replace(importantRE, '').trim()
+        this.el.style.setProperty(prop.kebab, value, isImportant)
+      } else {
+        this.el.style[prop.camel] = value
+      }
+    } else {
+      this.el.style[prop.camel] = ''
+    }
+  }
+
+}
+
+/**
+ * Normalize a CSS property name.
+ * - cache result
+ * - auto prefix
+ * - camelCase -> dash-case
+ *
+ * @param {String} prop
+ * @return {String}
+ */
+
+function normalize (prop) {
+  if (propCache[prop]) {
+    return propCache[prop]
+  }
+  var res = prefix(prop)
+  propCache[prop] = propCache[res] = res
+  return res
+}
+
+/**
+ * Auto detect the appropriate prefix for a CSS property.
+ * https://gist.github.com/paulirish/523692
+ *
+ * @param {String} prop
+ * @return {String}
+ */
+
+function prefix (prop) {
+  prop = hyphenate(prop)
+  var camel = camelize(prop)
+  var upper = camel.charAt(0).toUpperCase() + camel.slice(1)
+  if (!testEl) {
+    testEl = document.createElement('div')
+  }
+  var i = prefixes.length
+  var prefixed
+  if (camel !== 'filter' && (camel in testEl.style)) {
+    return {
+      kebab: prop,
+      camel: camel
+    }
+  }
+  while (i--) {
+    prefixed = camelPrefixes[i] + upper
+    if (prefixed in testEl.style) {
+      return {
+        kebab: prefixes[i] + prop,
+        camel: prefixed
+      }
+    }
+  }
+}
diff --git a/src/directives/internal/transition.js b/src/directives/internal/transition.js
new file mode 100644
index 00000000000..0069567fcf3
--- /dev/null
+++ b/src/directives/internal/transition.js
@@ -0,0 +1,19 @@
+import { resolveAsset, addClass, removeClass } from '../../util/index'
+import { TRANSITION } from '../priorities'
+import Transition from '../../transition/transition'
+
+export default {
+
+  priority: TRANSITION,
+
+  update (id, oldId) {
+    var el = this.el
+    // resolve on owner vm
+    var hooks = resolveAsset(this.vm.$options, 'transitions', id)
+    id = id || 'v'
+    oldId = oldId || 'v'
+    el.__v_trans = new Transition(el, id, hooks, this.vm)
+    removeClass(el, oldId + '-transition')
+    addClass(el, id + '-transition')
+  }
+}
diff --git a/src/directives/priorities.js b/src/directives/priorities.js
new file mode 100644
index 00000000000..40ddacb96c0
--- /dev/null
+++ b/src/directives/priorities.js
@@ -0,0 +1,10 @@
+export const ON = 700
+export const MODEL = 800
+export const BIND = 850
+export const TRANSITION = 1100
+export const EL = 1500
+export const COMPONENT = 1500
+export const PARTIAL = 1750
+export const IF = 2100
+export const FOR = 2200
+export const SLOT = 2300
diff --git a/src/directives/public/bind.js b/src/directives/public/bind.js
new file mode 100644
index 00000000000..e0ae55ff8b6
--- /dev/null
+++ b/src/directives/public/bind.js
@@ -0,0 +1,158 @@
+import { warn, setClass, camelize } from '../../util/index'
+import { BIND } from '../priorities'
+import vStyle from '../internal/style'
+import { tokensToExp } from '../../parsers/text'
+
+// xlink
+const xlinkNS = 'http://www.w3.org/1999/xlink'
+const xlinkRE = /^xlink:/
+
+// check for attributes that prohibit interpolations
+const disallowedInterpAttrRE = /^v-|^:|^@|^(?:is|transition|transition-mode|debounce|track-by|stagger|enter-stagger|leave-stagger)$/
+// these attributes should also set their corresponding properties
+// because they only affect the initial state of the element
+const attrWithPropsRE = /^(?:value|checked|selected|muted)$/
+// these attributes expect enumrated values of "true" or "false"
+// but are not boolean attributes
+const enumeratedAttrRE = /^(?:draggable|contenteditable|spellcheck)$/
+
+// these attributes should set a hidden property for
+// binding v-model to object values
+const modelProps = {
+  value: '_value',
+  'true-value': '_trueValue',
+  'false-value': '_falseValue'
+}
+
+export default {
+
+  priority: BIND,
+
+  bind () {
+    var attr = this.arg
+    var tag = this.el.tagName
+    // should be deep watch on object mode
+    if (!attr) {
+      this.deep = true
+    }
+    // handle interpolation bindings
+    const descriptor = this.descriptor
+    const tokens = descriptor.interp
+    if (tokens) {
+      // handle interpolations with one-time tokens
+      if (descriptor.hasOneTime) {
+        this.expression = tokensToExp(tokens, this._scope || this.vm)
+      }
+
+      // only allow binding on native attributes
+      if (
+        disallowedInterpAttrRE.test(attr) ||
+        (attr === 'name' && (tag === 'PARTIAL' || tag === 'SLOT'))
+      ) {
+        process.env.NODE_ENV !== 'production' && warn(
+          attr + '="' + descriptor.raw + '": ' +
+          'attribute interpolation is not allowed in Vue.js ' +
+          'directives and special attributes.',
+          this.vm
+        )
+        this.el.removeAttribute(attr)
+        this.invalid = true
+      }
+
+      /* istanbul ignore if */
+      if (process.env.NODE_ENV !== 'production') {
+        var raw = attr + '="' + descriptor.raw + '": '
+        // warn src
+        if (attr === 'src') {
+          warn(
+            raw + 'interpolation in "src" attribute will cause ' +
+            'a 404 request. Use v-bind:src instead.',
+            this.vm
+          )
+        }
+
+        // warn style
+        if (attr === 'style') {
+          warn(
+            raw + 'interpolation in "style" attribute will cause ' +
+            'the attribute to be discarded in Internet Explorer. ' +
+            'Use v-bind:style instead.',
+            this.vm
+          )
+        }
+      }
+    }
+  },
+
+  update (value) {
+    if (this.invalid) {
+      return
+    }
+    var attr = this.arg
+    if (this.arg) {
+      this.handleSingle(attr, value)
+    } else {
+      this.handleObject(value || {})
+    }
+  },
+
+  // share object handler with v-bind:class
+  handleObject: vStyle.handleObject,
+
+  handleSingle (attr, value) {
+    const el = this.el
+    const interp = this.descriptor.interp
+    if (this.modifiers.camel) {
+      attr = camelize(attr)
+    }
+    if (
+      !interp &&
+      attrWithPropsRE.test(attr) &&
+      attr in el
+    ) {
+      var attrValue = attr === 'value'
+        ? value == null // IE9 will set input.value to "null" for null...
+          ? ''
+          : value
+        : value
+
+      if (el[attr] !== attrValue) {
+        el[attr] = attrValue
+      }
+    }
+    // set model props
+    var modelProp = modelProps[attr]
+    if (!interp && modelProp) {
+      el[modelProp] = value
+      // update v-model if present
+      var model = el.__v_model
+      if (model) {
+        model.listener()
+      }
+    }
+    // do not set value attribute for textarea
+    if (attr === 'value' && el.tagName === 'TEXTAREA') {
+      el.removeAttribute(attr)
+      return
+    }
+    // update attribute
+    if (enumeratedAttrRE.test(attr)) {
+      el.setAttribute(attr, value ? 'true' : 'false')
+    } else if (value != null && value !== false) {
+      if (attr === 'class') {
+        // handle edge case #1960:
+        // class interpolation should not overwrite Vue transition class
+        if (el.__v_trans) {
+          value += ' ' + el.__v_trans.id + '-transition'
+        }
+        setClass(el, value)
+      } else if (xlinkRE.test(attr)) {
+        el.setAttributeNS(xlinkNS, attr, value === true ? '' : value)
+      } else {
+        el.setAttribute(attr, value === true ? '' : value)
+      }
+    } else {
+      el.removeAttribute(attr)
+    }
+  }
+}
diff --git a/src/directives/public/cloak.js b/src/directives/public/cloak.js
new file mode 100644
index 00000000000..803880f806f
--- /dev/null
+++ b/src/directives/public/cloak.js
@@ -0,0 +1,8 @@
+export default {
+  bind () {
+    var el = this.el
+    this.vm.$once('pre-hook:compiled', function () {
+      el.removeAttribute('v-cloak')
+    })
+  }
+}
diff --git a/src/directives/public/el.js b/src/directives/public/el.js
new file mode 100644
index 00000000000..0507128f617
--- /dev/null
+++ b/src/directives/public/el.js
@@ -0,0 +1,28 @@
+import { camelize, hasOwn, defineReactive } from '../../util/index'
+import { EL } from '../priorities'
+
+export default {
+
+  priority: EL,
+
+  bind () {
+    /* istanbul ignore if */
+    if (!this.arg) {
+      return
+    }
+    var id = this.id = camelize(this.arg)
+    var refs = (this._scope || this.vm).$els
+    if (hasOwn(refs, id)) {
+      refs[id] = this.el
+    } else {
+      defineReactive(refs, id, this.el)
+    }
+  },
+
+  unbind () {
+    var refs = (this._scope || this.vm).$els
+    if (refs[this.id] === this.el) {
+      refs[this.id] = null
+    }
+  }
+}
diff --git a/src/directives/public/for.js b/src/directives/public/for.js
new file mode 100644
index 00000000000..b66e41f6ba5
--- /dev/null
+++ b/src/directives/public/for.js
@@ -0,0 +1,678 @@
+import FragmentFactory from '../../fragment/factory'
+import { FOR } from '../priorities'
+import { withoutConversion } from '../../observer/index'
+import { getPath } from '../../parsers/path'
+import {
+  isObject,
+  warn,
+  createAnchor,
+  replace,
+  before,
+  after,
+  remove,
+  hasOwn,
+  inDoc,
+  defineReactive,
+  def,
+  cancellable,
+  isArray,
+  isPlainObject
+} from '../../util/index'
+
+let uid = 0
+
+const vFor = {
+
+  priority: FOR,
+  terminal: true,
+
+  params: [
+    'track-by',
+    'stagger',
+    'enter-stagger',
+    'leave-stagger'
+  ],
+
+  bind () {
+    if (process.env.NODE_ENV !== 'production' && this.el.hasAttribute('v-if')) {
+      warn(
+        `<${this.el.tagName.toLowerCase()} v-for="${this.expression}" v-if="${this.el.getAttribute('v-if')}">: ` +
+        `Using v-if and v-for on the same element is not recommended - ` +
+        `consider filtering the source Array instead.`,
+        this.vm
+      )
+    }
+
+    // support "item in/of items" syntax
+    var inMatch = this.expression.match(/(.*) (?:in|of) (.*)/)
+    if (inMatch) {
+      var itMatch = inMatch[1].match(/\((.*),(.*)\)/)
+      if (itMatch) {
+        this.iterator = itMatch[1].trim()
+        this.alias = itMatch[2].trim()
+      } else {
+        this.alias = inMatch[1].trim()
+      }
+      this.expression = inMatch[2]
+    }
+
+    if (!this.alias) {
+      process.env.NODE_ENV !== 'production' && warn(
+        'Invalid v-for expression "' + this.descriptor.raw + '": ' +
+        'alias is required.',
+        this.vm
+      )
+      return
+    }
+
+    // uid as a cache identifier
+    this.id = '__v-for__' + (++uid)
+
+    // check if this is an option list,
+    // so that we know if we need to update the <select>'s
+    // v-model when the option list has changed.
+    // because v-model has a lower priority than v-for,
+    // the v-model is not bound here yet, so we have to
+    // retrive it in the actual updateModel() function.
+    var tag = this.el.tagName
+    this.isOption =
+      (tag === 'OPTION' || tag === 'OPTGROUP') &&
+      this.el.parentNode.tagName === 'SELECT'
+
+    // setup anchor nodes
+    this.start = createAnchor('v-for-start')
+    this.end = createAnchor('v-for-end')
+    replace(this.el, this.end)
+    before(this.start, this.end)
+
+    // cache
+    this.cache = Object.create(null)
+
+    // fragment factory
+    this.factory = new FragmentFactory(this.vm, this.el)
+  },
+
+  update (data) {
+    this.diff(data)
+    this.updateRef()
+    this.updateModel()
+  },
+
+  /**
+   * Diff, based on new data and old data, determine the
+   * minimum amount of DOM manipulations needed to make the
+   * DOM reflect the new data Array.
+   *
+   * The algorithm diffs the new data Array by storing a
+   * hidden reference to an owner vm instance on previously
+   * seen data. This allows us to achieve O(n) which is
+   * better than a levenshtein distance based algorithm,
+   * which is O(m * n).
+   *
+   * @param {Array} data
+   */
+
+  diff (data) {
+    // check if the Array was converted from an Object
+    var item = data[0]
+    var convertedFromObject = this.fromObject =
+      isObject(item) &&
+      hasOwn(item, '$key') &&
+      hasOwn(item, '$value')
+
+    var trackByKey = this.params.trackBy
+    var oldFrags = this.frags
+    var frags = this.frags = new Array(data.length)
+    var alias = this.alias
+    var iterator = this.iterator
+    var start = this.start
+    var end = this.end
+    var inDocument = inDoc(start)
+    var init = !oldFrags
+    var i, l, frag, key, value, primitive
+
+    // First pass, go through the new Array and fill up
+    // the new frags array. If a piece of data has a cached
+    // instance for it, we reuse it. Otherwise build a new
+    // instance.
+    for (i = 0, l = data.length; i < l; i++) {
+      item = data[i]
+      key = convertedFromObject ? item.$key : null
+      value = convertedFromObject ? item.$value : item
+      primitive = !isObject(value)
+      frag = !init && this.getCachedFrag(value, i, key)
+      if (frag) { // reusable fragment
+        frag.reused = true
+        // update $index
+        frag.scope.$index = i
+        // update $key
+        if (key) {
+          frag.scope.$key = key
+        }
+        // update iterator
+        if (iterator) {
+          frag.scope[iterator] = key !== null ? key : i
+        }
+        // update data for track-by, object repeat &
+        // primitive values.
+        if (trackByKey || convertedFromObject || primitive) {
+          withoutConversion(() => {
+            frag.scope[alias] = value
+          })
+        }
+      } else { // new instance
+        frag = this.create(value, alias, i, key)
+        frag.fresh = !init
+      }
+      frags[i] = frag
+      if (init) {
+        frag.before(end)
+      }
+    }
+
+    // we're done for the initial render.
+    if (init) {
+      return
+    }
+
+    // Second pass, go through the old fragments and
+    // destroy those who are not reused (and remove them
+    // from cache)
+    var removalIndex = 0
+    var totalRemoved = oldFrags.length - frags.length
+    // when removing a large number of fragments, watcher removal
+    // turns out to be a perf bottleneck, so we batch the watcher
+    // removals into a single filter call!
+    this.vm._vForRemoving = true
+    for (i = 0, l = oldFrags.length; i < l; i++) {
+      frag = oldFrags[i]
+      if (!frag.reused) {
+        this.deleteCachedFrag(frag)
+        this.remove(frag, removalIndex++, totalRemoved, inDocument)
+      }
+    }
+    this.vm._vForRemoving = false
+    if (removalIndex) {
+      this.vm._watchers = this.vm._watchers.filter(w => w.active)
+    }
+
+    // Final pass, move/insert new fragments into the
+    // right place.
+    var targetPrev, prevEl, currentPrev
+    var insertionIndex = 0
+    for (i = 0, l = frags.length; i < l; i++) {
+      frag = frags[i]
+      // this is the frag that we should be after
+      targetPrev = frags[i - 1]
+      prevEl = targetPrev
+        ? targetPrev.staggerCb
+          ? targetPrev.staggerAnchor
+          : targetPrev.end || targetPrev.node
+        : start
+      if (frag.reused && !frag.staggerCb) {
+        currentPrev = findPrevFrag(frag, start, this.id)
+        if (
+          currentPrev !== targetPrev && (
+            !currentPrev ||
+            // optimization for moving a single item.
+            // thanks to suggestions by @livoras in #1807
+            findPrevFrag(currentPrev, start, this.id) !== targetPrev
+          )
+        ) {
+          this.move(frag, prevEl)
+        }
+      } else {
+        // new instance, or still in stagger.
+        // insert with updated stagger index.
+        this.insert(frag, insertionIndex++, prevEl, inDocument)
+      }
+      frag.reused = frag.fresh = false
+    }
+  },
+
+  /**
+   * Create a new fragment instance.
+   *
+   * @param {*} value
+   * @param {String} alias
+   * @param {Number} index
+   * @param {String} [key]
+   * @return {Fragment}
+   */
+
+  create (value, alias, index, key) {
+    var host = this._host
+    // create iteration scope
+    var parentScope = this._scope || this.vm
+    var scope = Object.create(parentScope)
+    // ref holder for the scope
+    scope.$refs = Object.create(parentScope.$refs)
+    scope.$els = Object.create(parentScope.$els)
+    // make sure point $parent to parent scope
+    scope.$parent = parentScope
+    // for two-way binding on alias
+    scope.$forContext = this
+    // define scope properties
+    // important: define the scope alias without forced conversion
+    // so that frozen data structures remain non-reactive.
+    withoutConversion(() => {
+      defineReactive(scope, alias, value)
+    })
+    defineReactive(scope, '$index', index)
+    if (key) {
+      defineReactive(scope, '$key', key)
+    } else if (scope.$key) {
+      // avoid accidental fallback
+      def(scope, '$key', null)
+    }
+    if (this.iterator) {
+      defineReactive(scope, this.iterator, key !== null ? key : index)
+    }
+    var frag = this.factory.create(host, scope, this._frag)
+    frag.forId = this.id
+    this.cacheFrag(value, frag, index, key)
+    return frag
+  },
+
+  /**
+   * Update the v-ref on owner vm.
+   */
+
+  updateRef () {
+    var ref = this.descriptor.ref
+    if (!ref) return
+    var hash = (this._scope || this.vm).$refs
+    var refs
+    if (!this.fromObject) {
+      refs = this.frags.map(findVmFromFrag)
+    } else {
+      refs = {}
+      this.frags.forEach(function (frag) {
+        refs[frag.scope.$key] = findVmFromFrag(frag)
+      })
+    }
+    hash[ref] = refs
+  },
+
+  /**
+   * For option lists, update the containing v-model on
+   * parent <select>.
+   */
+
+  updateModel () {
+    if (this.isOption) {
+      var parent = this.start.parentNode
+      var model = parent && parent.__v_model
+      if (model) {
+        model.forceUpdate()
+      }
+    }
+  },
+
+  /**
+   * Insert a fragment. Handles staggering.
+   *
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {Node} prevEl
+   * @param {Boolean} inDocument
+   */
+
+  insert (frag, index, prevEl, inDocument) {
+    if (frag.staggerCb) {
+      frag.staggerCb.cancel()
+      frag.staggerCb = null
+    }
+    var staggerAmount = this.getStagger(frag, index, null, 'enter')
+    if (inDocument && staggerAmount) {
+      // create an anchor and insert it synchronously,
+      // so that we can resolve the correct order without
+      // worrying about some elements not inserted yet
+      var anchor = frag.staggerAnchor
+      if (!anchor) {
+        anchor = frag.staggerAnchor = createAnchor('stagger-anchor')
+        anchor.__v_frag = frag
+      }
+      after(anchor, prevEl)
+      var op = frag.staggerCb = cancellable(function () {
+        frag.staggerCb = null
+        frag.before(anchor)
+        remove(anchor)
+      })
+      setTimeout(op, staggerAmount)
+    } else {
+      var target = prevEl.nextSibling
+      /* istanbul ignore if */
+      if (!target) {
+        // reset end anchor position in case the position was messed up
+        // by an external drag-n-drop library.
+        after(this.end, prevEl)
+        target = this.end
+      }
+      frag.before(target)
+    }
+  },
+
+  /**
+   * Remove a fragment. Handles staggering.
+   *
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {Number} total
+   * @param {Boolean} inDocument
+   */
+
+  remove (frag, index, total, inDocument) {
+    if (frag.staggerCb) {
+      frag.staggerCb.cancel()
+      frag.staggerCb = null
+      // it's not possible for the same frag to be removed
+      // twice, so if we have a pending stagger callback,
+      // it means this frag is queued for enter but removed
+      // before its transition started. Since it is already
+      // destroyed, we can just leave it in detached state.
+      return
+    }
+    var staggerAmount = this.getStagger(frag, index, total, 'leave')
+    if (inDocument && staggerAmount) {
+      var op = frag.staggerCb = cancellable(function () {
+        frag.staggerCb = null
+        frag.remove()
+      })
+      setTimeout(op, staggerAmount)
+    } else {
+      frag.remove()
+    }
+  },
+
+  /**
+   * Move a fragment to a new position.
+   * Force no transition.
+   *
+   * @param {Fragment} frag
+   * @param {Node} prevEl
+   */
+
+  move (frag, prevEl) {
+    // fix a common issue with Sortable:
+    // if prevEl doesn't have nextSibling, this means it's
+    // been dragged after the end anchor. Just re-position
+    // the end anchor to the end of the container.
+    /* istanbul ignore if */
+    if (!prevEl.nextSibling) {
+      this.end.parentNode.appendChild(this.end)
+    }
+    frag.before(prevEl.nextSibling, false)
+  },
+
+  /**
+   * Cache a fragment using track-by or the object key.
+   *
+   * @param {*} value
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {String} [key]
+   */
+
+  cacheFrag (value, frag, index, key) {
+    var trackByKey = this.params.trackBy
+    var cache = this.cache
+    var primitive = !isObject(value)
+    var id
+    if (key || trackByKey || primitive) {
+      id = getTrackByKey(index, key, value, trackByKey)
+      if (!cache[id]) {
+        cache[id] = frag
+      } else if (trackByKey !== '$index') {
+        process.env.NODE_ENV !== 'production' &&
+        this.warnDuplicate(value)
+      }
+    } else {
+      id = this.id
+      if (hasOwn(value, id)) {
+        if (value[id] === null) {
+          value[id] = frag
+        } else {
+          process.env.NODE_ENV !== 'production' &&
+          this.warnDuplicate(value)
+        }
+      } else if (Object.isExtensible(value)) {
+        def(value, id, frag)
+      } else if (process.env.NODE_ENV !== 'production') {
+        warn(
+          'Frozen v-for objects cannot be automatically tracked, make sure to ' +
+          'provide a track-by key.'
+        )
+      }
+    }
+    frag.raw = value
+  },
+
+  /**
+   * Get a cached fragment from the value/index/key
+   *
+   * @param {*} value
+   * @param {Number} index
+   * @param {String} key
+   * @return {Fragment}
+   */
+
+  getCachedFrag (value, index, key) {
+    var trackByKey = this.params.trackBy
+    var primitive = !isObject(value)
+    var frag
+    if (key || trackByKey || primitive) {
+      var id = getTrackByKey(index, key, value, trackByKey)
+      frag = this.cache[id]
+    } else {
+      frag = value[this.id]
+    }
+    if (frag && (frag.reused || frag.fresh)) {
+      process.env.NODE_ENV !== 'production' &&
+      this.warnDuplicate(value)
+    }
+    return frag
+  },
+
+  /**
+   * Delete a fragment from cache.
+   *
+   * @param {Fragment} frag
+   */
+
+  deleteCachedFrag (frag) {
+    var value = frag.raw
+    var trackByKey = this.params.trackBy
+    var scope = frag.scope
+    var index = scope.$index
+    // fix #948: avoid accidentally fall through to
+    // a parent repeater which happens to have $key.
+    var key = hasOwn(scope, '$key') && scope.$key
+    var primitive = !isObject(value)
+    if (trackByKey || key || primitive) {
+      var id = getTrackByKey(index, key, value, trackByKey)
+      this.cache[id] = null
+    } else {
+      value[this.id] = null
+      frag.raw = null
+    }
+  },
+
+  /**
+   * Get the stagger amount for an insertion/removal.
+   *
+   * @param {Fragment} frag
+   * @param {Number} index
+   * @param {Number} total
+   * @param {String} type
+   */
+
+  getStagger (frag, index, total, type) {
+    type = type + 'Stagger'
+    var trans = frag.node.__v_trans
+    var hooks = trans && trans.hooks
+    var hook = hooks && (hooks[type] || hooks.stagger)
+    return hook
+      ? hook.call(frag, index, total)
+      : index * parseInt(this.params[type] || this.params.stagger, 10)
+  },
+
+  /**
+   * Pre-process the value before piping it through the
+   * filters. This is passed to and called by the watcher.
+   */
+
+  _preProcess (value) {
+    // regardless of type, store the un-filtered raw value.
+    this.rawValue = value
+    return value
+  },
+
+  /**
+   * Post-process the value after it has been piped through
+   * the filters. This is passed to and called by the watcher.
+   *
+   * It is necessary for this to be called during the
+   * watcher's dependency collection phase because we want
+   * the v-for to update when the source Object is mutated.
+   */
+
+  _postProcess (value) {
+    if (isArray(value)) {
+      return value
+    } else if (isPlainObject(value)) {
+      // convert plain object to array.
+      var keys = Object.keys(value)
+      var i = keys.length
+      var res = new Array(i)
+      var key
+      while (i--) {
+        key = keys[i]
+        res[i] = {
+          $key: key,
+          $value: value[key]
+        }
+      }
+      return res
+    } else {
+      if (typeof value === 'number' && !isNaN(value)) {
+        value = range(value)
+      }
+      return value || []
+    }
+  },
+
+  unbind () {
+    if (this.descriptor.ref) {
+      (this._scope || this.vm).$refs[this.descriptor.ref] = null
+    }
+    if (this.frags) {
+      var i = this.frags.length
+      var frag
+      while (i--) {
+        frag = this.frags[i]
+        this.deleteCachedFrag(frag)
+        frag.destroy()
+      }
+    }
+  }
+}
+
+/**
+ * Helper to find the previous element that is a fragment
+ * anchor. This is necessary because a destroyed frag's
+ * element could still be lingering in the DOM before its
+ * leaving transition finishes, but its inserted flag
+ * should have been set to false so we can skip them.
+ *
+ * If this is a block repeat, we want to make sure we only
+ * return frag that is bound to this v-for. (see #929)
+ *
+ * @param {Fragment} frag
+ * @param {Comment|Text} anchor
+ * @param {String} id
+ * @return {Fragment}
+ */
+
+function findPrevFrag (frag, anchor, id) {
+  var el = frag.node.previousSibling
+  /* istanbul ignore if */
+  if (!el) return
+  frag = el.__v_frag
+  while (
+    (!frag || frag.forId !== id || !frag.inserted) &&
+    el !== anchor
+  ) {
+    el = el.previousSibling
+    /* istanbul ignore if */
+    if (!el) return
+    frag = el.__v_frag
+  }
+  return frag
+}
+
+/**
+ * Create a range array from given number.
+ *
+ * @param {Number} n
+ * @return {Array}
+ */
+
+function range (n) {
+  var i = -1
+  var ret = new Array(Math.floor(n))
+  while (++i < n) {
+    ret[i] = i
+  }
+  return ret
+}
+
+/**
+ * Get the track by key for an item.
+ *
+ * @param {Number} index
+ * @param {String} key
+ * @param {*} value
+ * @param {String} [trackByKey]
+ */
+
+function getTrackByKey (index, key, value, trackByKey) {
+  return trackByKey
+    ? trackByKey === '$index'
+      ? index
+      : trackByKey.charAt(0).match(/\w/)
+        ? getPath(value, trackByKey)
+        : value[trackByKey]
+    : (key || value)
+}
+
+if (process.env.NODE_ENV !== 'production') {
+  vFor.warnDuplicate = function (value) {
+    warn(
+      'Duplicate value found in v-for="' + this.descriptor.raw + '": ' +
+      JSON.stringify(value) + '. Use track-by="$index" if ' +
+      'you are expecting duplicate values.',
+      this.vm
+    )
+  }
+}
+
+/**
+ * Find a vm from a fragment.
+ *
+ * @param {Fragment} frag
+ * @return {Vue|undefined}
+ */
+
+function findVmFromFrag (frag) {
+  let node = frag.node
+  // handle multi-node frag
+  if (frag.end) {
+    while (!node.__vue__ && node !== frag.end && node.nextSibling) {
+      node = node.nextSibling
+    }
+  }
+  return node.__vue__
+}
+
+export default vFor
diff --git a/src/directives/public/html.js b/src/directives/public/html.js
new file mode 100644
index 00000000000..77dbbba37bd
--- /dev/null
+++ b/src/directives/public/html.js
@@ -0,0 +1,47 @@
+import { parseTemplate } from '../../parsers/template'
+import {
+  createAnchor,
+  before,
+  replace,
+  remove,
+  _toString,
+  toArray
+} from '../../util/index'
+
+export default {
+
+  bind () {
+    // a comment node means this is a binding for
+    // {{{ inline unescaped html }}}
+    if (this.el.nodeType === 8) {
+      // hold nodes
+      this.nodes = []
+      // replace the placeholder with proper anchor
+      this.anchor = createAnchor('v-html')
+      replace(this.el, this.anchor)
+    }
+  },
+
+  update (value) {
+    value = _toString(value)
+    if (this.nodes) {
+      this.swap(value)
+    } else {
+      this.el.innerHTML = value
+    }
+  },
+
+  swap (value) {
+    // remove old nodes
+    var i = this.nodes.length
+    while (i--) {
+      remove(this.nodes[i])
+    }
+    // convert new value to a fragment
+    // do not attempt to retrieve from id selector
+    var frag = parseTemplate(value, true, true)
+    // save a reference to these nodes so we can remove later
+    this.nodes = toArray(frag.childNodes)
+    before(frag, this.anchor)
+  }
+}
diff --git a/src/directives/public/if.js b/src/directives/public/if.js
new file mode 100644
index 00000000000..324e548ff9b
--- /dev/null
+++ b/src/directives/public/if.js
@@ -0,0 +1,87 @@
+import FragmentFactory from '../../fragment/factory'
+import { IF } from '../priorities'
+import {
+  getAttr,
+  remove,
+  replace,
+  createAnchor,
+  warn
+} from '../../util/index'
+
+export default {
+
+  priority: IF,
+  terminal: true,
+
+  bind () {
+    var el = this.el
+    if (!el.__vue__) {
+      // check else block
+      var next = el.nextElementSibling
+      if (next && getAttr(next, 'v-else') !== null) {
+        remove(next)
+        this.elseEl = next
+      }
+      // check main block
+      this.anchor = createAnchor('v-if')
+      replace(el, this.anchor)
+    } else {
+      process.env.NODE_ENV !== 'production' && warn(
+        'v-if="' + this.expression + '" cannot be ' +
+        'used on an instance root element.',
+        this.vm
+      )
+      this.invalid = true
+    }
+  },
+
+  update (value) {
+    if (this.invalid) return
+    if (value) {
+      if (!this.frag) {
+        this.insert()
+      }
+    } else {
+      this.remove()
+    }
+  },
+
+  insert () {
+    if (this.elseFrag) {
+      this.elseFrag.remove()
+      this.elseFrag = null
+    }
+    // lazy init factory
+    if (!this.factory) {
+      this.factory = new FragmentFactory(this.vm, this.el)
+    }
+    this.frag = this.factory.create(this._host, this._scope, this._frag)
+    this.frag.before(this.anchor)
+  },
+
+  remove () {
+    if (this.frag) {
+      this.frag.remove()
+      this.frag = null
+    }
+    if (this.elseEl && !this.elseFrag) {
+      if (!this.elseFactory) {
+        this.elseFactory = new FragmentFactory(
+          this.elseEl._context || this.vm,
+          this.elseEl
+        )
+      }
+      this.elseFrag = this.elseFactory.create(this._host, this._scope, this._frag)
+      this.elseFrag.before(this.anchor)
+    }
+  },
+
+  unbind () {
+    if (this.frag) {
+      this.frag.destroy()
+    }
+    if (this.elseFrag) {
+      this.elseFrag.destroy()
+    }
+  }
+}
diff --git a/src/directives/public/index.js b/src/directives/public/index.js
new file mode 100644
index 00000000000..50c957c390c
--- /dev/null
+++ b/src/directives/public/index.js
@@ -0,0 +1,33 @@
+// text & html
+import text from './text'
+import html from './html'
+// logic control
+import vFor from './for'
+import vIf from './if'
+import show from './show'
+// two-way binding
+import model from './model/index'
+// event handling
+import on from './on'
+// attributes
+import bind from './bind'
+// ref & el
+import el from './el'
+import ref from './ref'
+// cloak
+import cloak from './cloak'
+
+// must export plain object
+export default {
+  text,
+  html,
+  'for': vFor,
+  'if': vIf,
+  show,
+  model,
+  on,
+  bind,
+  el,
+  ref,
+  cloak
+}
diff --git a/src/directives/public/model/checkbox.js b/src/directives/public/model/checkbox.js
new file mode 100644
index 00000000000..ace92e62285
--- /dev/null
+++ b/src/directives/public/model/checkbox.js
@@ -0,0 +1,68 @@
+import {
+  toNumber,
+  isArray,
+  indexOf,
+  looseEqual
+} from '../../../util/index'
+
+export default {
+
+  bind () {
+    var self = this
+    var el = this.el
+
+    this.getValue = function () {
+      return el.hasOwnProperty('_value')
+        ? el._value
+        : self.params.number
+          ? toNumber(el.value)
+          : el.value
+    }
+
+    function getBooleanValue () {
+      var val = el.checked
+      if (val && el.hasOwnProperty('_trueValue')) {
+        return el._trueValue
+      }
+      if (!val && el.hasOwnProperty('_falseValue')) {
+        return el._falseValue
+      }
+      return val
+    }
+
+    this.listener = function () {
+      var model = self._watcher.get()
+      if (isArray(model)) {
+        var val = self.getValue()
+        var i = indexOf(model, val)
+        if (el.checked) {
+          if (i < 0) {
+            self.set(model.concat(val))
+          }
+        } else if (i > -1) {
+          self.set(model.slice(0, i).concat(model.slice(i + 1)))
+        }
+      } else {
+        self.set(getBooleanValue())
+      }
+    }
+
+    this.on('change', this.listener)
+    if (el.hasAttribute('checked')) {
+      this.afterBind = this.listener
+    }
+  },
+
+  update (value) {
+    var el = this.el
+    if (isArray(value)) {
+      el.checked = indexOf(value, this.getValue()) > -1
+    } else {
+      if (el.hasOwnProperty('_trueValue')) {
+        el.checked = looseEqual(value, el._trueValue)
+      } else {
+        el.checked = !!value
+      }
+    }
+  }
+}
diff --git a/src/directives/public/model/index.js b/src/directives/public/model/index.js
new file mode 100644
index 00000000000..e4f158b5699
--- /dev/null
+++ b/src/directives/public/model/index.js
@@ -0,0 +1,89 @@
+import { warn, resolveAsset } from '../../../util/index'
+import { MODEL } from '../../priorities'
+import text from './text'
+import radio from './radio'
+import select from './select'
+import checkbox from './checkbox'
+
+const handlers = {
+  text,
+  radio,
+  select,
+  checkbox
+}
+
+export default {
+
+  priority: MODEL,
+  twoWay: true,
+  handlers: handlers,
+  params: ['lazy', 'number', 'debounce'],
+
+  /**
+   * Possible elements:
+   *   <select>
+   *   <textarea>
+   *   <input type="*">
+   *     - text
+   *     - checkbox
+   *     - radio
+   *     - number
+   */
+
+  bind () {
+    // friendly warning...
+    this.checkFilters()
+    if (this.hasRead && !this.hasWrite) {
+      process.env.NODE_ENV !== 'production' && warn(
+        'It seems you are using a read-only filter with ' +
+        'v-model="' + this.descriptor.raw + '". ' +
+        'You might want to use a two-way filter to ensure correct behavior.',
+        this.vm
+      )
+    }
+    var el = this.el
+    var tag = el.tagName
+    var handler
+    if (tag === 'INPUT') {
+      handler = handlers[el.type] || handlers.text
+    } else if (tag === 'SELECT') {
+      handler = handlers.select
+    } else if (tag === 'TEXTAREA') {
+      handler = handlers.text
+    } else {
+      process.env.NODE_ENV !== 'production' && warn(
+        'v-model does not support element type: ' + tag,
+        this.vm
+      )
+      return
+    }
+    el.__v_model = this
+    handler.bind.call(this)
+    this.update = handler.update
+    this._unbind = handler.unbind
+  },
+
+  /**
+   * Check read/write filter stats.
+   */
+
+  checkFilters () {
+    var filters = this.filters
+    if (!filters) return
+    var i = filters.length
+    while (i--) {
+      var filter = resolveAsset(this.vm.$options, 'filters', filters[i].name)
+      if (typeof filter === 'function' || filter.read) {
+        this.hasRead = true
+      }
+      if (filter.write) {
+        this.hasWrite = true
+      }
+    }
+  },
+
+  unbind () {
+    this.el.__v_model = null
+    this._unbind && this._unbind()
+  }
+}
diff --git a/src/directives/public/model/radio.js b/src/directives/public/model/radio.js
new file mode 100644
index 00000000000..454f731ce95
--- /dev/null
+++ b/src/directives/public/model/radio.js
@@ -0,0 +1,34 @@
+import { toNumber, looseEqual } from '../../../util/index'
+
+export default {
+
+  bind () {
+    var self = this
+    var el = this.el
+
+    this.getValue = function () {
+      // value overwrite via v-bind:value
+      if (el.hasOwnProperty('_value')) {
+        return el._value
+      }
+      var val = el.value
+      if (self.params.number) {
+        val = toNumber(val)
+      }
+      return val
+    }
+
+    this.listener = function () {
+      self.set(self.getValue())
+    }
+    this.on('change', this.listener)
+
+    if (el.hasAttribute('checked')) {
+      this.afterBind = this.listener
+    }
+  },
+
+  update (value) {
+    this.el.checked = looseEqual(value, this.getValue())
+  }
+}
diff --git a/src/directives/public/model/select.js b/src/directives/public/model/select.js
new file mode 100644
index 00000000000..8629a67d15f
--- /dev/null
+++ b/src/directives/public/model/select.js
@@ -0,0 +1,129 @@
+import {
+  inDoc,
+  isArray,
+  toNumber,
+  looseEqual,
+  nextTick
+} from '../../../util/index'
+
+export default {
+
+  bind () {
+    var self = this
+    var el = this.el
+
+    // method to force update DOM using latest value.
+    this.forceUpdate = function () {
+      if (self._watcher) {
+        self.update(self._watcher.get())
+      }
+    }
+
+    // check if this is a multiple select
+    var multiple = this.multiple = el.hasAttribute('multiple')
+
+    // attach listener
+    this.listener = function () {
+      var value = getValue(el, multiple)
+      value = self.params.number
+        ? isArray(value)
+          ? value.map(toNumber)
+          : toNumber(value)
+        : value
+      self.set(value)
+    }
+    this.on('change', this.listener)
+
+    // if has initial value, set afterBind
+    var initValue = getValue(el, multiple, true)
+    if ((multiple && initValue.length) ||
+        (!multiple && initValue !== null)) {
+      this.afterBind = this.listener
+    }
+
+    // All major browsers except Firefox resets
+    // selectedIndex with value -1 to 0 when the element
+    // is appended to a new parent, therefore we have to
+    // force a DOM update whenever that happens...
+    this.vm.$on('hook:attached', () => {
+      nextTick(this.forceUpdate)
+    })
+    if (!inDoc(el)) {
+      nextTick(this.forceUpdate)
+    }
+  },
+
+  update (value) {
+    var el = this.el
+    el.selectedIndex = -1
+    var multi = this.multiple && isArray(value)
+    var options = el.options
+    var i = options.length
+    var op, val
+    while (i--) {
+      op = options[i]
+      val = op.hasOwnProperty('_value')
+        ? op._value
+        : op.value
+      /* eslint-disable eqeqeq */
+      op.selected = multi
+        ? indexOf(value, val) > -1
+        : looseEqual(value, val)
+      /* eslint-enable eqeqeq */
+    }
+  },
+
+  unbind () {
+    /* istanbul ignore next */
+    this.vm.$off('hook:attached', this.forceUpdate)
+  }
+}
+
+/**
+ * Get select value
+ *
+ * @param {SelectElement} el
+ * @param {Boolean} multi
+ * @param {Boolean} init
+ * @return {Array|*}
+ */
+
+function getValue (el, multi, init) {
+  var res = multi ? [] : null
+  var op, val, selected
+  for (var i = 0, l = el.options.length; i < l; i++) {
+    op = el.options[i]
+    selected = init
+      ? op.hasAttribute('selected')
+      : op.selected
+    if (selected) {
+      val = op.hasOwnProperty('_value')
+        ? op._value
+        : op.value
+      if (multi) {
+        res.push(val)
+      } else {
+        return val
+      }
+    }
+  }
+  return res
+}
+
+/**
+ * Native Array.indexOf uses strict equal, but in this
+ * case we need to match string/numbers with custom equal.
+ *
+ * @param {Array} arr
+ * @param {*} val
+ */
+
+function indexOf (arr, val) {
+  var i = arr.length
+  while (i--) {
+    if (looseEqual(arr[i], val)) {
+      return i
+    }
+  }
+  return -1
+}
diff --git a/src/directives/public/model/text.js b/src/directives/public/model/text.js
new file mode 100644
index 00000000000..ed56aba5c36
--- /dev/null
+++ b/src/directives/public/model/text.js
@@ -0,0 +1,146 @@
+/* global jQuery */
+
+import {
+  isIE9,
+  isAndroid,
+  toNumber,
+  _toString,
+  nextTick,
+  debounce as _debounce
+} from '../../../util/index'
+
+export default {
+
+  bind () {
+    var self = this
+    var el = this.el
+    var isRange = el.type === 'range'
+    var lazy = this.params.lazy
+    var number = this.params.number
+    var debounce = this.params.debounce
+
+    // handle composition events.
+    //   http://blog.evanyou.me/2014/01/03/composition-event/
+    // skip this for Android because it handles composition
+    // events quite differently. Android doesn't trigger
+    // composition events for language input methods e.g.
+    // Chinese, but instead triggers them for spelling
+    // suggestions... (see Discussion/#162)
+    var composing = false
+    if (!isAndroid && !isRange) {
+      this.on('compositionstart', function () {
+        composing = true
+      })
+      this.on('compositionend', function () {
+        composing = false
+        // in IE11 the "compositionend" event fires AFTER
+        // the "input" event, so the input handler is blocked
+        // at the end... have to call it here.
+        //
+        // #1327: in lazy mode this is unecessary.
+        if (!lazy) {
+          self.listener()
+        }
+      })
+    }
+
+    // prevent messing with the input when user is typing,
+    // and force update on blur.
+    this.focused = false
+    if (!isRange && !lazy) {
+      this.on('focus', function () {
+        self.focused = true
+      })
+      this.on('blur', function () {
+        self.focused = false
+        // do not sync value after fragment removal (#2017)
+        if (!self._frag || self._frag.inserted) {
+          self.rawListener()
+        }
+      })
+    }
+
+    // Now attach the main listener
+    this.listener = this.rawListener = function () {
+      if (composing || !self._bound) {
+        return
+      }
+      var val = number || isRange
+        ? toNumber(el.value)
+        : el.value
+      self.set(val)
+      // force update on next tick to avoid lock & same value
+      // also only update when user is not typing
+      nextTick(function () {
+        if (self._bound && !self.focused) {
+          self.update(self._watcher.value)
+        }
+      })
+    }
+
+    // apply debounce
+    if (debounce) {
+      this.listener = _debounce(this.listener, debounce)
+    }
+
+    // Support jQuery events, since jQuery.trigger() doesn't
+    // trigger native events in some cases and some plugins
+    // rely on $.trigger()
+    //
+    // We want to make sure if a listener is attached using
+    // jQuery, it is also removed with jQuery, that's why
+    // we do the check for each directive instance and
+    // store that check result on itself. This also allows
+    // easier test coverage control by unsetting the global
+    // jQuery variable in tests.
+    this.hasjQuery = typeof jQuery === 'function'
+    if (this.hasjQuery) {
+      const method = jQuery.fn.on ? 'on' : 'bind'
+      jQuery(el)[method]('change', this.rawListener)
+      if (!lazy) {
+        jQuery(el)[method]('input', this.listener)
+      }
+    } else {
+      this.on('change', this.rawListener)
+      if (!lazy) {
+        this.on('input', this.listener)
+      }
+    }
+
+    // IE9 doesn't fire input event on backspace/del/cut
+    if (!lazy && isIE9) {
+      this.on('cut', function () {
+        nextTick(self.listener)
+      })
+      this.on('keyup', function (e) {
+        if (e.keyCode === 46 || e.keyCode === 8) {
+          self.listener()
+        }
+      })
+    }
+
+    // set initial value if present
+    if (
+      el.hasAttribute('value') ||
+      (el.tagName === 'TEXTAREA' && el.value.trim())
+    ) {
+      this.afterBind = this.listener
+    }
+  },
+
+  update (value) {
+    // #3029 only update when the value changes. This prevent
+    // browsers from overwriting values like selectionStart
+    value = _toString(value)
+    if (value !== this.el.value) this.el.value = value
+  },
+
+  unbind () {
+    var el = this.el
+    if (this.hasjQuery) {
+      const method = jQuery.fn.off ? 'off' : 'unbind'
+      jQuery(el)[method]('change', this.listener)
+      jQuery(el)[method]('input', this.listener)
+    }
+  }
+}
diff --git a/src/directives/public/on.js b/src/directives/public/on.js
new file mode 100644
index 00000000000..1a326b14ef4
--- /dev/null
+++ b/src/directives/public/on.js
@@ -0,0 +1,171 @@
+import { on, off, warn } from '../../util/index'
+import { ON } from '../priorities'
+
+// keyCode aliases
+const keyCodes = {
+  esc: 27,
+  tab: 9,
+  enter: 13,
+  space: 32,
+  'delete': [8, 46],
+  up: 38,
+  left: 37,
+  right: 39,
+  down: 40
+}
+
+const eventKeys = ['stop', 'prevent', 'self', 'capture']
+const modifierKeys = ['ctrl', 'shift', 'alt', 'meta']
+const namedKeys = eventKeys.concat(modifierKeys).sort()
+
+var modifierKeyHandlers = {}
+
+function keyFilter (handler, keys) {
+  var codes = keys.map(function (key) {
+    var charCode = key.charCodeAt(0)
+    if (charCode > 47 && charCode < 58) {
+      return parseInt(key, 10)
+    }
+    if (key.length === 1) {
+      charCode = key.toUpperCase().charCodeAt(0)
+      if (charCode > 64 && charCode < 91) {
+        return charCode
+      }
+    }
+    return keyCodes[key]
+  })
+  codes = [].concat.apply([], codes)
+  return function keyHandler (e) {
+    if (codes.indexOf(e.keyCode) > -1) {
+      return handler.call(this, e)
+    }
+  }
+}
+
+function stopFilter (handler) {
+  return function stopHandler (e) {
+    e.stopPropagation()
+    return handler.call(this, e)
+  }
+}
+
+function preventFilter (handler) {
+  return function preventHandler (e) {
+    e.preventDefault()
+    return handler.call(this, e)
+  }
+}
+
+function selfFilter (handler) {
+  return function selfHandler (e) {
+    if (e.target === e.currentTarget) {
+      return handler.call(this, e)
+    }
+  }
+}
+
+modifierKeys.forEach((key) => {
+  var keyName = key + 'Key'
+  modifierKeyHandlers[key] = function (handler) {
+    return function (e) {
+      if (e[keyName]) {
+        return handler.call(this, e)
+      }
+    }
+  }
+})
+
+export default {
+
+  priority: ON,
+  acceptStatement: true,
+  keyCodes,
+
+  bind () {
+    // deal with iframes
+    if (
+      this.el.tagName === 'IFRAME' &&
+      this.arg !== 'load'
+    ) {
+      var self = this
+      this.iframeBind = function () {
+        on(
+          self.el.contentWindow,
+          self.arg,
+          self.handler,
+          self.modifiers.capture
+        )
+      }
+      this.on('load', this.iframeBind)
+    }
+  },
+
+  update (handler) {
+    // stub a noop for v-on with no value,
+    // e.g. @mousedown.prevent
+    if (!this.descriptor.raw) {
+      handler = function () {}
+    }
+
+    if (typeof handler !== 'function') {
+      process.env.NODE_ENV !== 'production' && warn(
+        'v-on:' + this.arg + '="' +
+        this.expression + '" expects a function value, ' +
+        'got ' + handler,
+        this.vm
+      )
+      return
+    }
+
+    // apply modifiers
+    if (this.modifiers.stop) {
+      handler = stopFilter(handler)
+    }
+    if (this.modifiers.prevent) {
+      handler = preventFilter(handler)
+    }
+    if (this.modifiers.self) {
+      handler = selfFilter(handler)
+    }
+    modifierKeys.forEach((key) => {
+      if (this.modifiers[key]) {
+        handler = modifierKeyHandlers[key](handler)
+      }
+    })
+    // key filter
+    var keys = Object.keys(this.modifiers)
+      .filter(function (key) {
+        return namedKeys.indexOf(key) === -1
+      })
+    if (keys.length) {
+      handler = keyFilter(handler, keys)
+    }
+
+    this.reset()
+    this.handler = handler
+
+    if (this.iframeBind) {
+      this.iframeBind()
+    } else {
+      on(
+        this.el,
+        this.arg,
+        this.handler,
+        this.modifiers.capture
+      )
+    }
+  },
+
+  reset () {
+    var el = this.iframeBind
+      ? this.el.contentWindow
+      : this.el
+    if (this.handler) {
+      off(el, this.arg, this.handler)
+    }
+  },
+
+  unbind () {
+    this.reset()
+  }
+}
diff --git a/src/directives/public/ref.js b/src/directives/public/ref.js
new file mode 100644
index 00000000000..bf6660ab7f8
--- /dev/null
+++ b/src/directives/public/ref.js
@@ -0,0 +1,11 @@
+import { warn } from '../../util/index'
+
+export default {
+  bind () {
+    process.env.NODE_ENV !== 'production' && warn(
+      'v-ref:' + this.arg + ' must be used on a child ' +
+      'component. Found on <' + this.el.tagName.toLowerCase() + '>.',
+      this.vm
+    )
+  }
+}
diff --git a/src/directives/public/show.js b/src/directives/public/show.js
new file mode 100644
index 00000000000..e5ace01bd64
--- /dev/null
+++ b/src/directives/public/show.js
@@ -0,0 +1,31 @@
+import { getAttr, inDoc } from '../../util/index'
+import { applyTransition } from '../../transition/index'
+
+export default {
+
+  bind () {
+    // check else block
+    var next = this.el.nextElementSibling
+    if (next && getAttr(next, 'v-else') !== null) {
+      this.elseEl = next
+    }
+  },
+
+  update (value) {
+    this.apply(this.el, value)
+    if (this.elseEl) {
+      this.apply(this.elseEl, !value)
+    }
+  },
+
+  apply (el, value) {
+    if (inDoc(el)) {
+      applyTransition(el, value ? 1 : -1, toggle, this.vm)
+    } else {
+      toggle()
+    }
+    function toggle () {
+      el.style.display = value ? '' : 'none'
+    }
+  }
+}
diff --git a/src/directives/public/text.js b/src/directives/public/text.js
new file mode 100644
index 00000000000..c75bb14139c
--- /dev/null
+++ b/src/directives/public/text.js
@@ -0,0 +1,14 @@
+import { _toString } from '../../util/index'
+
+export default {
+
+  bind () {
+    this.attr = this.el.nodeType === 3
+      ? 'data'
+      : 'textContent'
+  },
+
+  update (value) {
+    this.el[this.attr] = _toString(value)
+  }
+}
diff --git a/src/filters/array-filters.js b/src/filters/array-filters.js
new file mode 100644
index 00000000000..aac702336b0
--- /dev/null
+++ b/src/filters/array-filters.js
@@ -0,0 +1,158 @@
+import { getPath } from '../parsers/path'
+import vFor from '../directives/public/for'
+import {
+  toArray,
+  toNumber,
+  isArray,
+  isObject,
+  isPlainObject
+} from '../util/index'
+const convertArray = vFor._postProcess
+
+/**
+ * Limit filter for arrays
+ *
+ * @param {Number} n
+ * @param {Number} offset (Decimal expected)
+ */
+
+export function limitBy (arr, n, offset) {
+  offset = offset ? parseInt(offset, 10) : 0
+  n = toNumber(n)
+  return typeof n === 'number'
+    ? arr.slice(offset, offset + n)
+    : arr
+}
+
+/**
+ * Filter filter for arrays
+ *
+ * @param {String} search
+ * @param {String} [delimiter]
+ * @param {String} ...dataKeys
+ */
+
+export function filterBy (arr, search, delimiter) {
+  arr = convertArray(arr)
+  if (search == null) {
+    return arr
+  }
+  if (typeof search === 'function') {
+    return arr.filter(search)
+  }
+  // cast to lowercase string
+  search = ('' + search).toLowerCase()
+  // allow optional `in` delimiter
+  // because why not
+  var n = delimiter === 'in' ? 3 : 2
+  // extract and flatten keys
+  var keys = Array.prototype.concat.apply([], toArray(arguments, n))
+  var res = []
+  var item, key, val, j
+  for (var i = 0, l = arr.length; i < l; i++) {
+    item = arr[i]
+    val = (item && item.$value) || item
+    j = keys.length
+    if (j) {
+      while (j--) {
+        key = keys[j]
+        if ((key === '$key' && contains(item.$key, search)) ||
+            contains(getPath(val, key), search)) {
+          res.push(item)
+          break
+        }
+      }
+    } else if (contains(item, search)) {
+      res.push(item)
+    }
+  }
+  return res
+}
+
+/**
+ * Order filter for arrays
+ *
+ * @param {String|Array<String>|Function} ...sortKeys
+ * @param {Number} [order]
+ */
+
+export function orderBy (arr) {
+  let comparator = null
+  let sortKeys
+  arr = convertArray(arr)
+
+  // determine order (last argument)
+  let args = toArray(arguments, 1)
+  let order = args[args.length - 1]
+  if (typeof order === 'number') {
+    order = order < 0 ? -1 : 1
+    args = args.length > 1 ? args.slice(0, -1) : args
+  } else {
+    order = 1
+  }
+
+  // determine sortKeys & comparator
+  const firstArg = args[0]
+  if (!firstArg) {
+    return arr
+  } else if (typeof firstArg === 'function') {
+    // custom comparator
+    comparator = function (a, b) {
+      return firstArg(a, b) * order
+    }
+  } else {
+    // string keys. flatten first
+    sortKeys = Array.prototype.concat.apply([], args)
+    comparator = function (a, b, i) {
+      i = i || 0
+      return i >= sortKeys.length - 1
+        ? baseCompare(a, b, i)
+        : baseCompare(a, b, i) || comparator(a, b, i + 1)
+    }
+  }
+
+  function baseCompare (a, b, sortKeyIndex) {
+    const sortKey = sortKeys[sortKeyIndex]
+    if (sortKey) {
+      if (sortKey !== '$key') {
+        if (isObject(a) && '$value' in a) a = a.$value
+        if (isObject(b) && '$value' in b) b = b.$value
+      }
+      a = isObject(a) ? getPath(a, sortKey) : a
+      b = isObject(b) ? getPath(b, sortKey) : b
+    }
+    return a === b ? 0 : a > b ? order : -order
+  }
+
+  // sort on a copy to avoid mutating original array
+  return arr.slice().sort(comparator)
+}
+
+/**
+ * String contain helper
+ *
+ * @param {*} val
+ * @param {String} search
+ */
+
+function contains (val, search) {
+  var i
+  if (isPlainObject(val)) {
+    var keys = Object.keys(val)
+    i = keys.length
+    while (i--) {
+      if (contains(val[keys[i]], search)) {
+        return true
+      }
+    }
+  } else if (isArray(val)) {
+    i = val.length
+    while (i--) {
+      if (contains(val[i], search)) {
+        return true
+      }
+    }
+  } else if (val != null) {
+    return val.toString().toLowerCase().indexOf(search) > -1
+  }
+}
diff --git a/src/filters/index.js b/src/filters/index.js
new file mode 100644
index 00000000000..92d9e218653
--- /dev/null
+++ b/src/filters/index.js
@@ -0,0 +1,131 @@
+import { toArray, debounce as _debounce } from '../util/index'
+import { orderBy, filterBy, limitBy } from './array-filters'
+const digitsRE = /(\d{3})(?=\d)/g
+
+// asset collections must be a plain object.
+export default {
+
+  orderBy,
+  filterBy,
+  limitBy,
+
+  /**
+   * Stringify value.
+   *
+   * @param {Number} indent
+   */
+
+  json: {
+    read: function (value, indent) {
+      return typeof value === 'string'
+        ? value
+        : JSON.stringify(value, null, arguments.length > 1 ? indent : 2)
+    },
+    write: function (value) {
+      try {
+        return JSON.parse(value)
+      } catch (e) {
+        return value
+      }
+    }
+  },
+
+  /**
+   * 'abc' => 'Abc'
+   */
+
+  capitalize (value) {
+    if (!value && value !== 0) return ''
+    value = value.toString()
+    return value.charAt(0).toUpperCase() + value.slice(1)
+  },
+
+  /**
+   * 'abc' => 'ABC'
+   */
+
+  uppercase (value) {
+    return (value || value === 0)
+      ? value.toString().toUpperCase()
+      : ''
+  },
+
+  /**
+   * 'AbC' => 'abc'
+   */
+
+  lowercase (value) {
+    return (value || value === 0)
+      ? value.toString().toLowerCase()
+      : ''
+  },
+
+  /**
+   * 12345 => $12,345.00
+   *
+   * @param {String} sign
+   * @param {Number} decimals Decimal places
+   */
+
+  currency (value, currency, decimals) {
+    value = parseFloat(value)
+    if (!isFinite(value) || (!value && value !== 0)) return ''
+    currency = currency != null ? currency : '$'
+    decimals = decimals != null ? decimals : 2
+    var stringified = Math.abs(value).toFixed(decimals)
+    var _int = decimals
+      ? stringified.slice(0, -1 - decimals)
+      : stringified
+    var i = _int.length % 3
+    var head = i > 0
+      ? (_int.slice(0, i) + (_int.length > 3 ? ',' : ''))
+      : ''
+    var _float = decimals
+      ? stringified.slice(-1 - decimals)
+      : ''
+    var sign = value < 0 ? '-' : ''
+    return sign + currency + head +
+      _int.slice(i).replace(digitsRE, '$1,') +
+      _float
+  },
+
+  /**
+   * 'item' => 'items'
+   *
+   * @params
+   *  an array of strings corresponding to
+   *  the single, double, triple ... forms of the word to
+   *  be pluralized. When the number to be pluralized
+   *  exceeds the length of the args, it will use the last
+   *  entry in the array.
+   *
+   *  e.g. ['single', 'double', 'triple', 'multiple']
+   */
+
+  pluralize (value) {
+    var args = toArray(arguments, 1)
+    var length = args.length
+    if (length > 1) {
+      var index = value % 10 - 1
+      return index in args ? args[index] : args[length - 1]
+    } else {
+      return args[0] + (value === 1 ? '' : 's')
+    }
+  },
+
+  /**
+   * Debounce a handler function.
+   *
+   * @param {Function} handler
+   * @param {Number} delay = 300
+   * @return {Function}
+   */
+
+  debounce (handler, delay) {
+    if (!handler) return
+    if (!delay) {
+      delay = 300
+    }
+    return _debounce(handler, delay)
+  }
+}
diff --git a/src/fragment/factory.js b/src/fragment/factory.js
new file mode 100644
index 00000000000..c11b2dcc77d
--- /dev/null
+++ b/src/fragment/factory.js
@@ -0,0 +1,55 @@
+import { compile } from '../compiler/index'
+import { isTemplate, getOuterHTML } from '../util/index'
+import { parseTemplate, cloneNode } from '../parsers/template'
+import Fragment from './fragment'
+import Cache from '../cache'
+
+const linkerCache = new Cache(5000)
+
+/**
+ * A factory that can be used to create instances of a
+ * fragment. Caches the compiled linker if possible.
+ *
+ * @param {Vue} vm
+ * @param {Element|String} el
+ */
+
+export default function FragmentFactory (vm, el) {
+  this.vm = vm
+  var template
+  var isString = typeof el === 'string'
+  if (isString || isTemplate(el) && !el.hasAttribute('v-if')) {
+    template = parseTemplate(el, true)
+  } else {
+    template = document.createDocumentFragment()
+    template.appendChild(el)
+  }
+  this.template = template
+  // linker can be cached, but only for components
+  var linker
+  var cid = vm.constructor.cid
+  if (cid > 0) {
+    var cacheId = cid + (isString ? el : getOuterHTML(el))
+    linker = linkerCache.get(cacheId)
+    if (!linker) {
+      linker = compile(template, vm.$options, true)
+      linkerCache.put(cacheId, linker)
+    }
+  } else {
+    linker = compile(template, vm.$options, true)
+  }
+  this.linker = linker
+}
+
+/**
+ * Create a fragment instance with given host and scope.
+ *
+ * @param {Vue} host
+ * @param {Object} scope
+ * @param {Fragment} parentFrag
+ */
+
+FragmentFactory.prototype.create = function (host, scope, parentFrag) {
+  var frag = cloneNode(this.template)
+  return new Fragment(this.linker, this.vm, frag, host, scope, parentFrag)
+}
diff --git a/src/fragment/fragment.js b/src/fragment/fragment.js
new file mode 100644
index 00000000000..c9000507de0
--- /dev/null
+++ b/src/fragment/fragment.js
@@ -0,0 +1,211 @@
+import {
+  createAnchor,
+  before,
+  prepend,
+  inDoc,
+  mapNodeRange,
+  removeNodeRange
+} from '../util/index'
+
+import {
+  beforeWithTransition,
+  removeWithTransition
+} from '../transition/index'
+
+/**
+ * Abstraction for a partially-compiled fragment.
+ * Can optionally compile content with a child scope.
+ *
+ * @param {Function} linker
+ * @param {Vue} vm
+ * @param {DocumentFragment} frag
+ * @param {Vue} [host]
+ * @param {Object} [scope]
+ * @param {Fragment} [parentFrag]
+ */
+
+export default function Fragment (linker, vm, frag, host, scope, parentFrag) {
+  this.children = []
+  this.childFrags = []
+  this.vm = vm
+  this.scope = scope
+  this.inserted = false
+  this.parentFrag = parentFrag
+  if (parentFrag) {
+    parentFrag.childFrags.push(this)
+  }
+  this.unlink = linker(vm, frag, host, scope, this)
+  var single = this.single =
+    frag.childNodes.length === 1 &&
+    // do not go single mode if the only node is an anchor
+    !(frag.childNodes[0].__v_anchor)
+  if (single) {
+    this.node = frag.childNodes[0]
+    this.before = singleBefore
+    this.remove = singleRemove
+  } else {
+    this.node = createAnchor('fragment-start')
+    this.end = createAnchor('fragment-end')
+    this.frag = frag
+    prepend(this.node, frag)
+    frag.appendChild(this.end)
+    this.before = multiBefore
+    this.remove = multiRemove
+  }
+  this.node.__v_frag = this
+}
+
+/**
+ * Call attach/detach for all components contained within
+ * this fragment. Also do so recursively for all child
+ * fragments.
+ *
+ * @param {Function} hook
+ */
+
+Fragment.prototype.callHook = function (hook) {
+  var i, l
+  for (i = 0, l = this.childFrags.length; i < l; i++) {
+    this.childFrags[i].callHook(hook)
+  }
+  for (i = 0, l = this.children.length; i < l; i++) {
+    hook(this.children[i])
+  }
+}
+
+/**
+ * Insert fragment before target, single node version
+ *
+ * @param {Node} target
+ * @param {Boolean} withTransition
+ */
+
+function singleBefore (target, withTransition) {
+  this.inserted = true
+  var method = withTransition !== false
+    ? beforeWithTransition
+    : before
+  method(this.node, target, this.vm)
+  if (inDoc(this.node)) {
+    this.callHook(attach)
+  }
+}
+
+/**
+ * Remove fragment, single node version
+ */
+
+function singleRemove () {
+  this.inserted = false
+  var shouldCallRemove = inDoc(this.node)
+  var self = this
+  this.beforeRemove()
+  removeWithTransition(this.node, this.vm, function () {
+    if (shouldCallRemove) {
+      self.callHook(detach)
+    }
+    self.destroy()
+  })
+}
+
+/**
+ * Insert fragment before target, multi-nodes version
+ *
+ * @param {Node} target
+ * @param {Boolean} withTransition
+ */
+
+function multiBefore (target, withTransition) {
+  this.inserted = true
+  var vm = this.vm
+  var method = withTransition !== false
+    ? beforeWithTransition
+    : before
+  mapNodeRange(this.node, this.end, function (node) {
+    method(node, target, vm)
+  })
+  if (inDoc(this.node)) {
+    this.callHook(attach)
+  }
+}
+
+/**
+ * Remove fragment, multi-nodes version
+ */
+
+function multiRemove () {
+  this.inserted = false
+  var self = this
+  var shouldCallRemove = inDoc(this.node)
+  this.beforeRemove()
+  removeNodeRange(this.node, this.end, this.vm, this.frag, function () {
+    if (shouldCallRemove) {
+      self.callHook(detach)
+    }
+    self.destroy()
+  })
+}
+
+/**
+ * Prepare the fragment for removal.
+ */
+
+Fragment.prototype.beforeRemove = function () {
+  var i, l
+  for (i = 0, l = this.childFrags.length; i < l; i++) {
+    // call the same method recursively on child
+    // fragments, depth-first
+    this.childFrags[i].beforeRemove(false)
+  }
+  for (i = 0, l = this.children.length; i < l; i++) {
+    // Call destroy for all contained instances,
+    // with remove:false and defer:true.
+    // Defer is necessary because we need to
+    // keep the children to call detach hooks
+    // on them.
+    this.children[i].$destroy(false, true)
+  }
+  var dirs = this.unlink.dirs
+  for (i = 0, l = dirs.length; i < l; i++) {
+    // disable the watchers on all the directives
+    // so that the rendered content stays the same
+    // during removal.
+    dirs[i]._watcher && dirs[i]._watcher.teardown()
+  }
+}
+
+/**
+ * Destroy the fragment.
+ */
+
+Fragment.prototype.destroy = function () {
+  if (this.parentFrag) {
+    this.parentFrag.childFrags.$remove(this)
+  }
+  this.node.__v_frag = null
+  this.unlink()
+}
+
+/**
+ * Call attach hook for a Vue instance.
+ *
+ * @param {Vue} child
+ */
+
+function attach (child) {
+  if (!child._isAttached && inDoc(child.$el)) {
+    child._callHook('attached')
+  }
+}
+
+/**
+ * Call detach hook for a Vue instance.
+ *
+ * @param {Vue} child
+ */
+
+function detach (child) {
+  if (child._isAttached && !inDoc(child.$el)) {
+    child._callHook('detached')
+  }
+}
diff --git a/src/global-api.js b/src/global-api.js
new file mode 100644
index 00000000000..a5999a0c02a
--- /dev/null
+++ b/src/global-api.js
@@ -0,0 +1,226 @@
+import config from './config'
+import directives from './directives/public/index'
+import elementDirectives from './directives/element/index'
+import filters from './filters/index'
+import * as util from './util/index'
+import * as compiler from './compiler/index'
+import * as path from './parsers/path'
+import * as text from './parsers/text'
+import * as template from './parsers/template'
+import * as directive from './parsers/directive'
+import * as expression from './parsers/expression'
+import * as transition from './transition/index'
+import FragmentFactory from './fragment/factory'
+import internalDirectives from './directives/internal/index'
+
+import {
+  set,
+  del,
+  nextTick,
+  mergeOptions,
+  classify,
+  toArray,
+  commonTagRE,
+  reservedTagRE,
+  warn,
+  isPlainObject,
+  extend
+} from './util/index'
+
+export default function (Vue) {
+  /**
+   * Vue and every constructor that extends Vue has an
+   * associated options object, which can be accessed during
+   * compilation steps as `this.constructor.options`.
+   *
+   * These can be seen as the default options of every
+   * Vue instance.
+   */
+
+  Vue.options = {
+    directives,
+    elementDirectives,
+    filters,
+    transitions: {},
+    components: {},
+    partials: {},
+    replace: true
+  }
+
+  /**
+   * Expose useful internals
+   */
+
+  Vue.util = util
+  Vue.config = config
+  Vue.set = set
+  Vue.delete = del
+  Vue.nextTick = nextTick
+
+  /**
+   * The following are exposed for advanced usage / plugins
+   */
+
+  Vue.compiler = compiler
+  Vue.FragmentFactory = FragmentFactory
+  Vue.internalDirectives = internalDirectives
+  Vue.parsers = {
+    path,
+    text,
+    template,
+    directive,
+    expression
+  }
+
+  /**
+   * Each instance constructor, including Vue, has a unique
+   * cid. This enables us to create wrapped "child
+   * constructors" for prototypal inheritance and cache them.
+   */
+
+  Vue.cid = 0
+  var cid = 1
+
+  /**
+   * Class inheritance
+   *
+   * @param {Object} extendOptions
+   */
+
+  Vue.extend = function (extendOptions) {
+    extendOptions = extendOptions || {}
+    var Super = this
+    var isFirstExtend = Super.cid === 0
+    if (isFirstExtend && extendOptions._Ctor) {
+      return extendOptions._Ctor
+    }
+    var name = extendOptions.name || Super.options.name
+    if (process.env.NODE_ENV !== 'production') {
+      if (!/^[a-zA-Z][\w-]*$/.test(name)) {
+        warn(
+          'Invalid component name: "' + name + '". Component names ' +
+          'can only contain alphanumeric characaters and the hyphen.'
+        )
+        name = null
+      }
+    }
+    var Sub = createClass(name || 'VueComponent')
+    Sub.prototype = Object.create(Super.prototype)
+    Sub.prototype.constructor = Sub
+    Sub.cid = cid++
+    Sub.options = mergeOptions(
+      Super.options,
+      extendOptions
+    )
+    Sub['super'] = Super
+    // allow further extension
+    Sub.extend = Super.extend
+    // create asset registers, so extended classes
+    // can have their private assets too.
+    config._assetTypes.forEach(function (type) {
+      Sub[type] = Super[type]
+    })
+    // enable recursive self-lookup
+    if (name) {
+      Sub.options.components[name] = Sub
+    }
+    // cache constructor
+    if (isFirstExtend) {
+      extendOptions._Ctor = Sub
+    }
+    return Sub
+  }
+
+  /**
+   * A function that returns a sub-class constructor with the
+   * given name. This gives us much nicer output when
+   * logging instances in the console.
+   *
+   * @param {String} name
+   * @return {Function}
+   */
+
+  function createClass (name) {
+    /* eslint-disable no-new-func */
+    return new Function(
+      'return function ' + classify(name) +
+      ' (options) { this._init(options) }'
+    )()
+    /* eslint-enable no-new-func */
+  }
+
+  /**
+   * Plugin system
+   *
+   * @param {Object} plugin
+   */
+
+  Vue.use = function (plugin) {
+    /* istanbul ignore if */
+    if (plugin.installed) {
+      return
+    }
+    // additional parameters
+    var args = toArray(arguments, 1)
+    args.unshift(this)
+    if (typeof plugin.install === 'function') {
+      plugin.install.apply(plugin, args)
+    } else {
+      plugin.apply(null, args)
+    }
+    plugin.installed = true
+    return this
+  }
+
+  /**
+   * Apply a global mixin by merging it into the default
+   * options.
+   */
+
+  Vue.mixin = function (mixin) {
+    Vue.options = mergeOptions(Vue.options, mixin)
+  }
+
+  /**
+   * Create asset registration methods with the following
+   * signature:
+   *
+   * @param {String} id
+   * @param {*} definition
+   */
+
+  config._assetTypes.forEach(function (type) {
+    Vue[type] = function (id, definition) {
+      if (!definition) {
+        return this.options[type + 's'][id]
+      } else {
+        /* istanbul ignore if */
+        if (process.env.NODE_ENV !== 'production') {
+          if (
+            type === 'component' &&
+            (commonTagRE.test(id) || reservedTagRE.test(id))
+          ) {
+            warn(
+              'Do not use built-in or reserved HTML elements as component ' +
+              'id: ' + id
+            )
+          }
+        }
+        if (
+          type === 'component' &&
+          isPlainObject(definition)
+        ) {
+          if (!definition.name) {
+            definition.name = id
+          }
+          definition = Vue.extend(definition)
+        }
+        this.options[type + 's'][id] = definition
+        return definition
+      }
+    }
+  })
+
+  // expose internal transition API
+  extend(Vue.transition, transition)
+}
diff --git a/src/global.d.ts b/src/global.d.ts
deleted file mode 100644
index badbd1e02e8..00000000000
--- a/src/global.d.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-declare const __DEV__: boolean
-declare const __TEST__: boolean
-declare const __GLOBAL__: boolean
-
-interface Window {
-  __VUE_DEVTOOLS_GLOBAL_HOOK__: DevtoolsHook
-}
-
-// from https://github.com/vuejs/vue-devtools/blob/bc719c95a744614f5c3693460b64dc21dfa339a8/packages/app-backend-api/src/global-hook.ts#L3
-interface DevtoolsHook {
-  emit: (event: string, ...payload: any[]) => void
-  on: (event: string, handler: Function) => void
-  once: (event: string, handler: Function) => void
-  off: (event?: string, handler?: Function) => void
-  Vue?: any
-  // apps: AppRecordOptions[]
-}
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 00000000000..c15102dca84
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,28 @@
+import Vue from './instance/vue'
+import installGlobalAPI from './global-api'
+import { inBrowser, devtools } from './util/index'
+import config from './config'
+
+installGlobalAPI(Vue)
+
+Vue.version = '1.0.28'
+
+export default Vue
+
+// devtools global hook
+/* istanbul ignore next */
+setTimeout(() => {
+  if (config.devtools) {
+    if (devtools) {
+      devtools.emit('init', Vue)
+    } else if (
+      process.env.NODE_ENV !== 'production' &&
+      inBrowser && /Chrome\/\d+/.test(window.navigator.userAgent)
+    ) {
+      console.log(
+        'Download the Vue Devtools for a better development experience:\n' +
+        'https://github.com/vuejs/vue-devtools'
+      )
+    }
+  }
+}, 0)
diff --git a/src/instance/api/data.js b/src/instance/api/data.js
new file mode 100644
index 00000000000..7e84404b24c
--- /dev/null
+++ b/src/instance/api/data.js
@@ -0,0 +1,188 @@
+import Watcher from '../../watcher'
+import { del, toArray } from '../../util/index'
+import { parseText } from '../../parsers/text'
+import { parseDirective } from '../../parsers/directive'
+import { getPath } from '../../parsers/path'
+import { parseExpression } from '../../parsers/expression'
+
+const filterRE = /[^|]\|[^|]/
+
+export default function (Vue) {
+  /**
+   * Get the value from an expression on this vm.
+   *
+   * @param {String} exp
+   * @param {Boolean} [asStatement]
+   * @return {*}
+   */
+
+  Vue.prototype.$get = function (exp, asStatement) {
+    var res = parseExpression(exp)
+    if (res) {
+      if (asStatement) {
+        var self = this
+        return function statementHandler () {
+          self.$arguments = toArray(arguments)
+          var result = res.get.call(self, self)
+          self.$arguments = null
+          return result
+        }
+      } else {
+        try {
+          return res.get.call(this, this)
+        } catch (e) {}
+      }
+    }
+  }
+
+  /**
+   * Set the value from an expression on this vm.
+   * The expression must be a valid left-hand
+   * expression in an assignment.
+   *
+   * @param {String} exp
+   * @param {*} val
+   */
+
+  Vue.prototype.$set = function (exp, val) {
+    var res = parseExpression(exp, true)
+    if (res && res.set) {
+      res.set.call(this, this, val)
+    }
+  }
+
+  /**
+   * Delete a property on the VM
+   *
+   * @param {String} key
+   */
+
+  Vue.prototype.$delete = function (key) {
+    del(this._data, key)
+  }
+
+  /**
+   * Watch an expression, trigger callback when its
+   * value changes.
+   *
+   * @param {String|Function} expOrFn
+   * @param {Function} cb
+   * @param {Object} [options]
+   *                 - {Boolean} deep
+   *                 - {Boolean} immediate
+   * @return {Function} - unwatchFn
+   */
+
+  Vue.prototype.$watch = function (expOrFn, cb, options) {
+    var vm = this
+    var parsed
+    if (typeof expOrFn === 'string') {
+      parsed = parseDirective(expOrFn)
+      expOrFn = parsed.expression
+    }
+    var watcher = new Watcher(vm, expOrFn, cb, {
+      deep: options && options.deep,
+      sync: options && options.sync,
+      filters: parsed && parsed.filters,
+      user: !options || options.user !== false
+    })
+    if (options && options.immediate) {
+      cb.call(vm, watcher.value)
+    }
+    return function unwatchFn () {
+      watcher.teardown()
+    }
+  }
+
+  /**
+   * Evaluate a text directive, including filters.
+   *
+   * @param {String} text
+   * @param {Boolean} [asStatement]
+   * @return {String}
+   */
+
+  Vue.prototype.$eval = function (text, asStatement) {
+    // check for filters.
+    if (filterRE.test(text)) {
+      var dir = parseDirective(text)
+      // the filter regex check might give false positive
+      // for pipes inside strings, so it's possible that
+      // we don't get any filters here
+      var val = this.$get(dir.expression, asStatement)
+      return dir.filters
+        ? this._applyFilters(val, null, dir.filters)
+        : val
+    } else {
+      // no filter
+      return this.$get(text, asStatement)
+    }
+  }
+
+  /**
+   * Interpolate a piece of template text.
+   *
+   * @param {String} text
+   * @return {String}
+   */
+
+  Vue.prototype.$interpolate = function (text) {
+    var tokens = parseText(text)
+    var vm = this
+    if (tokens) {
+      if (tokens.length === 1) {
+        return vm.$eval(tokens[0].value) + ''
+      } else {
+        return tokens.map(function (token) {
+          return token.tag
+            ? vm.$eval(token.value)
+            : token.value
+        }).join('')
+      }
+    } else {
+      return text
+    }
+  }
+
+  /**
+   * Log instance data as a plain JS object
+   * so that it is easier to inspect in console.
+   * This method assumes console is available.
+   *
+   * @param {String} [path]
+   */
+
+  Vue.prototype.$log = function (path) {
+    var data = path
+      ? getPath(this._data, path)
+      : this._data
+    if (data) {
+      data = clean(data)
+    }
+    // include computed fields
+    if (!path) {
+      var key
+      for (key in this.$options.computed) {
+        data[key] = clean(this[key])
+      }
+      if (this._props) {
+        for (key in this._props) {
+          data[key] = clean(this[key])
+        }
+      }
+    }
+    console.log(data)
+  }
+
+  /**
+   * "clean" a getter/setter converted object into a plain
+   * object copy.
+   *
+   * @param {Object} - obj
+   * @return {Object}
+   */
+
+  function clean (obj) {
+    return JSON.parse(JSON.stringify(obj))
+  }
+}
diff --git a/src/instance/api/dom.js b/src/instance/api/dom.js
new file mode 100644
index 00000000000..11d85e5944f
--- /dev/null
+++ b/src/instance/api/dom.js
@@ -0,0 +1,218 @@
+import {
+  nextTick,
+  inDoc,
+  removeNodeRange,
+  mapNodeRange,
+  before,
+  remove
+} from '../../util/index'
+
+import {
+  beforeWithTransition,
+  appendWithTransition,
+  removeWithTransition
+} from '../../transition/index'
+
+export default function (Vue) {
+  /**
+   * Convenience on-instance nextTick. The callback is
+   * auto-bound to the instance, and this avoids component
+   * modules having to rely on the global Vue.
+   *
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$nextTick = function (fn) {
+    nextTick(fn, this)
+  }
+
+  /**
+   * Append instance to target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$appendTo = function (target, cb, withTransition) {
+    return insert(
+      this, target, cb, withTransition,
+      append, appendWithTransition
+    )
+  }
+
+  /**
+   * Prepend instance to target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$prependTo = function (target, cb, withTransition) {
+    target = query(target)
+    if (target.hasChildNodes()) {
+      this.$before(target.firstChild, cb, withTransition)
+    } else {
+      this.$appendTo(target, cb, withTransition)
+    }
+    return this
+  }
+
+  /**
+   * Insert instance before target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$before = function (target, cb, withTransition) {
+    return insert(
+      this, target, cb, withTransition,
+      beforeWithCb, beforeWithTransition
+    )
+  }
+
+  /**
+   * Insert instance after target
+   *
+   * @param {Node} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$after = function (target, cb, withTransition) {
+    target = query(target)
+    if (target.nextSibling) {
+      this.$before(target.nextSibling, cb, withTransition)
+    } else {
+      this.$appendTo(target.parentNode, cb, withTransition)
+    }
+    return this
+  }
+
+  /**
+   * Remove instance from DOM
+   *
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition] - defaults to true
+   */
+
+  Vue.prototype.$remove = function (cb, withTransition) {
+    if (!this.$el.parentNode) {
+      return cb && cb()
+    }
+    var inDocument = this._isAttached && inDoc(this.$el)
+    // if we are not in document, no need to check
+    // for transitions
+    if (!inDocument) withTransition = false
+    var self = this
+    var realCb = function () {
+      if (inDocument) self._callHook('detached')
+      if (cb) cb()
+    }
+    if (this._isFragment) {
+      removeNodeRange(
+        this._fragmentStart,
+        this._fragmentEnd,
+        this, this._fragment, realCb
+      )
+    } else {
+      var op = withTransition === false
+        ? removeWithCb
+        : removeWithTransition
+      op(this.$el, this, realCb)
+    }
+    return this
+  }
+
+  /**
+   * Shared DOM insertion function.
+   *
+   * @param {Vue} vm
+   * @param {Element} target
+   * @param {Function} [cb]
+   * @param {Boolean} [withTransition]
+   * @param {Function} op1 - op for non-transition insert
+   * @param {Function} op2 - op for transition insert
+   * @return vm
+   */
+
+  function insert (vm, target, cb, withTransition, op1, op2) {
+    target = query(target)
+    var targetIsDetached = !inDoc(target)
+    var op = withTransition === false || targetIsDetached
+        ? op1
+        : op2
+    var shouldCallHook =
+      !targetIsDetached &&
+      !vm._isAttached &&
+      !inDoc(vm.$el)
+    if (vm._isFragment) {
+      mapNodeRange(vm._fragmentStart, vm._fragmentEnd, function (node) {
+        op(node, target, vm)
+      })
+      cb && cb()
+    } else {
+      op(vm.$el, target, vm, cb)
+    }
+    if (shouldCallHook) {
+      vm._callHook('attached')
+    }
+    return vm
+  }
+
+  /**
+   * Check for selectors
+   *
+   * @param {String|Element} el
+   */
+
+  function query (el) {
+    return typeof el === 'string'
+      ? document.querySelector(el)
+      : el
+  }
+
+  /**
+   * Append operation that takes a callback.
+   *
+   * @param {Node} el
+   * @param {Node} target
+   * @param {Vue} vm - unused
+   * @param {Function} [cb]
+   */
+
+  function append (el, target, vm, cb) {
+    target.appendChild(el)
+    if (cb) cb()
+  }
+
+  /**
+   * InsertBefore operation that takes a callback.
+   *
+   * @param {Node} el
+   * @param {Node} target
+   * @param {Vue} vm - unused
+   * @param {Function} [cb]
+   */
+
+  function beforeWithCb (el, target, vm, cb) {
+    before(el, target)
+    if (cb) cb()
+  }
+
+  /**
+   * Remove operation that takes a callback.
+   *
+   * @param {Node} el
+   * @param {Vue} vm - unused
+   * @param {Function} [cb]
+   */
+
+  function removeWithCb (el, vm, cb) {
+    remove(el)
+    if (cb) cb()
+  }
+}
diff --git a/src/instance/api/events.js b/src/instance/api/events.js
new file mode 100644
index 00000000000..672b1e78a57
--- /dev/null
+++ b/src/instance/api/events.js
@@ -0,0 +1,202 @@
+import { toArray } from '../../util/index'
+
+export default function (Vue) {
+  /**
+   * Listen on the given `event` with `fn`.
+   *
+   * @param {String} event
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$on = function (event, fn) {
+    (this._events[event] || (this._events[event] = []))
+      .push(fn)
+    modifyListenerCount(this, event, 1)
+    return this
+  }
+
+  /**
+   * Adds an `event` listener that will be invoked a single
+   * time then automatically removed.
+   *
+   * @param {String} event
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$once = function (event, fn) {
+    var self = this
+    function on () {
+      self.$off(event, on)
+      fn.apply(this, arguments)
+    }
+    on.fn = fn
+    this.$on(event, on)
+    return this
+  }
+
+  /**
+   * Remove the given callback for `event` or all
+   * registered callbacks.
+   *
+   * @param {String} event
+   * @param {Function} fn
+   */
+
+  Vue.prototype.$off = function (event, fn) {
+    var cbs
+    // all
+    if (!arguments.length) {
+      if (this.$parent) {
+        for (event in this._events) {
+          cbs = this._events[event]
+          if (cbs) {
+            modifyListenerCount(this, event, -cbs.length)
+          }
+        }
+      }
+      this._events = {}
+      return this
+    }
+    // specific event
+    cbs = this._events[event]
+    if (!cbs) {
+      return this
+    }
+    if (arguments.length === 1) {
+      modifyListenerCount(this, event, -cbs.length)
+      this._events[event] = null
+      return this
+    }
+    // specific handler
+    var cb
+    var i = cbs.length
+    while (i--) {
+      cb = cbs[i]
+      if (cb === fn || cb.fn === fn) {
+        modifyListenerCount(this, event, -1)
+        cbs.splice(i, 1)
+        break
+      }
+    }
+    return this
+  }
+
+  /**
+   * Trigger an event on self.
+   *
+   * @param {String|Object} event
+   * @return {Boolean} shouldPropagate
+   */
+
+  Vue.prototype.$emit = function (event) {
+    var isSource = typeof event === 'string'
+    event = isSource
+      ? event
+      : event.name
+    var cbs = this._events[event]
+    var shouldPropagate = isSource || !cbs
+    if (cbs) {
+      cbs = cbs.length > 1
+        ? toArray(cbs)
+        : cbs
+      // this is a somewhat hacky solution to the question raised
+      // in #2102: for an inline component listener like <comp @test="doThis">,
+      // the propagation handling is somewhat broken. Therefore we
+      // need to treat these inline callbacks differently.
+      var hasParentCbs = isSource && cbs.some(function (cb) {
+        return cb._fromParent
+      })
+      if (hasParentCbs) {
+        shouldPropagate = false
+      }
+      var args = toArray(arguments, 1)
+      for (var i = 0, l = cbs.length; i < l; i++) {
+        var cb = cbs[i]
+        var res = cb.apply(this, args)
+        if (res === true && (!hasParentCbs || cb._fromParent)) {
+          shouldPropagate = true
+        }
+      }
+    }
+    return shouldPropagate
+  }
+
+  /**
+   * Recursively broadcast an event to all children instances.
+   *
+   * @param {String|Object} event
+   * @param {...*} additional arguments
+   */
+
+  Vue.prototype.$broadcast = function (event) {
+    var isSource = typeof event === 'string'
+    event = isSource
+      ? event
+      : event.name
+    // if no child has registered for this event,
+    // then there's no need to broadcast.
+    if (!this._eventsCount[event]) return
+    var children = this.$children
+    var args = toArray(arguments)
+    if (isSource) {
+      // use object event to indicate non-source emit
+      // on children
+      args[0] = { name: event, source: this }
+    }
+    for (var i = 0, l = children.length; i < l; i++) {
+      var child = children[i]
+      var shouldPropagate = child.$emit.apply(child, args)
+      if (shouldPropagate) {
+        child.$broadcast.apply(child, args)
+      }
+    }
+    return this
+  }
+
+  /**
+   * Recursively propagate an event up the parent chain.
+   *
+   * @param {String} event
+   * @param {...*} additional arguments
+   */
+
+  Vue.prototype.$dispatch = function (event) {
+    var shouldPropagate = this.$emit.apply(this, arguments)
+    if (!shouldPropagate) return
+    var parent = this.$parent
+    var args = toArray(arguments)
+    // use object event to indicate non-source emit
+    // on parents
+    args[0] = { name: event, source: this }
+    while (parent) {
+      shouldPropagate = parent.$emit.apply(parent, args)
+      parent = shouldPropagate
+        ? parent.$parent
+        : null
+    }
+    return this
+  }
+
+  /**
+   * Modify the listener counts on all parents.
+   * This bookkeeping allows $broadcast to return early when
+   * no child has listened to a certain event.
+   *
+   * @param {Vue} vm
+   * @param {String} event
+   * @param {Number} count
+   */
+
+  var hookRE = /^hook:/
+  function modifyListenerCount (vm, event, count) {
+    var parent = vm.$parent
+    // hooks do not get broadcasted so no need
+    // to do bookkeeping for them
+    if (!parent || !count || hookRE.test(event)) return
+    while (parent) {
+      parent._eventsCount[event] =
+        (parent._eventsCount[event] || 0) + count
+      parent = parent.$parent
+    }
+  }
+}
diff --git a/src/instance/api/lifecycle.js b/src/instance/api/lifecycle.js
new file mode 100644
index 00000000000..99ddc80c372
--- /dev/null
+++ b/src/instance/api/lifecycle.js
@@ -0,0 +1,76 @@
+import { warn, query, inDoc } from '../../util/index'
+import { compile } from '../../compiler/index'
+
+export default function (Vue) {
+  /**
+   * Set instance target element and kick off the compilation
+   * process. The passed in `el` can be a selector string, an
+   * existing Element, or a DocumentFragment (for block
+   * instances).
+   *
+   * @param {Element|DocumentFragment|string} el
+   * @public
+   */
+
+  Vue.prototype.$mount = function (el) {
+    if (this._isCompiled) {
+      process.env.NODE_ENV !== 'production' && warn(
+        '$mount() should be called only once.',
+        this
+      )
+      return
+    }
+    el = query(el)
+    if (!el) {
+      el = document.createElement('div')
+    }
+    this._compile(el)
+    this._initDOMHooks()
+    if (inDoc(this.$el)) {
+      this._callHook('attached')
+      ready.call(this)
+    } else {
+      this.$once('hook:attached', ready)
+    }
+    return this
+  }
+
+  /**
+   * Mark an instance as ready.
+   */
+
+  function ready () {
+    this._isAttached = true
+    this._isReady = true
+    this._callHook('ready')
+  }
+
+  /**
+   * Teardown the instance, simply delegate to the internal
+   * _destroy.
+   *
+   * @param {Boolean} remove
+   * @param {Boolean} deferCleanup
+   */
+
+  Vue.prototype.$destroy = function (remove, deferCleanup) {
+    this._destroy(remove, deferCleanup)
+  }
+
+  /**
+   * Partially compile a piece of DOM and return a
+   * decompile function.
+   *
+   * @param {Element|DocumentFragment} el
+   * @param {Vue} [host]
+   * @param {Object} [scope]
+   * @param {Fragment} [frag]
+   * @return {Function}
+   */
+
+  Vue.prototype.$compile = function (el, host, scope, frag) {
+    return compile(el, this.$options, true)(
+      this, el, host, scope, frag
+    )
+  }
+}
diff --git a/src/instance/internal/events.js b/src/instance/internal/events.js
new file mode 100644
index 00000000000..d2ab49a1e5b
--- /dev/null
+++ b/src/instance/internal/events.js
@@ -0,0 +1,180 @@
+import { isSimplePath } from '../../parsers/expression'
+import {
+  inDoc,
+  isArray,
+  warn
+} from '../../util/index'
+
+const eventRE = /^v-on:|^@/
+
+export default function (Vue) {
+  /**
+   * Setup the instance's option events & watchers.
+   * If the value is a string, we pull it from the
+   * instance's methods by name.
+   */
+
+  Vue.prototype._initEvents = function () {
+    var options = this.$options
+    if (options._asComponent) {
+      registerComponentEvents(this, options.el)
+    }
+    registerCallbacks(this, '$on', options.events)
+    registerCallbacks(this, '$watch', options.watch)
+  }
+
+  /**
+   * Register v-on events on a child component
+   *
+   * @param {Vue} vm
+   * @param {Element} el
+   */
+
+  function registerComponentEvents (vm, el) {
+    var attrs = el.attributes
+    var name, value, handler
+    for (var i = 0, l = attrs.length; i < l; i++) {
+      name = attrs[i].name
+      if (eventRE.test(name)) {
+        name = name.replace(eventRE, '')
+        // force the expression into a statement so that
+        // it always dynamically resolves the method to call (#2670)
+        // kinda ugly hack, but does the job.
+        value = attrs[i].value
+        if (isSimplePath(value)) {
+          value += '.apply(this, $arguments)'
+        }
+        handler = (vm._scope || vm._context).$eval(value, true)
+        handler._fromParent = true
+        vm.$on(name.replace(eventRE), handler)
+      }
+    }
+  }
+
+  /**
+   * Register callbacks for option events and watchers.
+   *
+   * @param {Vue} vm
+   * @param {String} action
+   * @param {Object} hash
+   */
+
+  function registerCallbacks (vm, action, hash) {
+    if (!hash) return
+    var handlers, key, i, j
+    for (key in hash) {
+      handlers = hash[key]
+      if (isArray(handlers)) {
+        for (i = 0, j = handlers.length; i < j; i++) {
+          register(vm, action, key, handlers[i])
+        }
+      } else {
+        register(vm, action, key, handlers)
+      }
+    }
+  }
+
+  /**
+   * Helper to register an event/watch callback.
+   *
+   * @param {Vue} vm
+   * @param {String} action
+   * @param {String} key
+   * @param {Function|String|Object} handler
+   * @param {Object} [options]
+   */
+
+  function register (vm, action, key, handler, options) {
+    var type = typeof handler
+    if (type === 'function') {
+      vm[action](key, handler, options)
+    } else if (type === 'string') {
+      var methods = vm.$options.methods
+      var method = methods && methods[handler]
+      if (method) {
+        vm[action](key, method, options)
+      } else {
+        process.env.NODE_ENV !== 'production' && warn(
+          'Unknown method: "' + handler + '" when ' +
+          'registering callback for ' + action +
+          ': "' + key + '".',
+          vm
+        )
+      }
+    } else if (handler && type === 'object') {
+      register(vm, action, key, handler.handler, handler)
+    }
+  }
+
+  /**
+   * Setup recursive attached/detached calls
+   */
+
+  Vue.prototype._initDOMHooks = function () {
+    this.$on('hook:attached', onAttached)
+    this.$on('hook:detached', onDetached)
+  }
+
+  /**
+   * Callback to recursively call attached hook on children
+   */
+
+  function onAttached () {
+    if (!this._isAttached) {
+      this._isAttached = true
+      this.$children.forEach(callAttach)
+    }
+  }
+
+  /**
+   * Iterator to call attached hook
+   *
+   * @param {Vue} child
+   */
+
+  function callAttach (child) {
+    if (!child._isAttached && inDoc(child.$el)) {
+      child._callHook('attached')
+    }
+  }
+
+  /**
+   * Callback to recursively call detached hook on children
+   */
+
+  function onDetached () {
+    if (this._isAttached) {
+      this._isAttached = false
+      this.$children.forEach(callDetach)
+    }
+  }
+
+  /**
+   * Iterator to call detached hook
+   *
+   * @param {Vue} child
+   */
+
+  function callDetach (child) {
+    if (child._isAttached && !inDoc(child.$el)) {
+      child._callHook('detached')
+    }
+  }
+
+  /**
+   * Trigger all handlers for a hook
+   *
+   * @param {String} hook
+   */
+
+  Vue.prototype._callHook = function (hook) {
+    this.$emit('pre-hook:' + hook)
+    var handlers = this.$options[hook]
+    if (handlers) {
+      for (var i = 0, j = handlers.length; i < j; i++) {
+        handlers[i].call(this)
+      }
+    }
+    this.$emit('hook:' + hook)
+  }
+}
diff --git a/src/instance/internal/init.js b/src/instance/internal/init.js
new file mode 100644
index 00000000000..705a48e6cca
--- /dev/null
+++ b/src/instance/internal/init.js
@@ -0,0 +1,114 @@
+import { mergeOptions } from '../../util/index'
+
+let uid = 0
+
+export default function (Vue) {
+  /**
+   * The main init sequence. This is called for every
+   * instance, including ones that are created from extended
+   * constructors.
+   *
+   * @param {Object} options - this options object should be
+   *                           the result of merging class
+   *                           options and the options passed
+   *                           in to the constructor.
+   */
+
+  Vue.prototype._init = function (options) {
+    options = options || {}
+
+    this.$el = null
+    this.$parent = options.parent
+    this.$root = this.$parent
+      ? this.$parent.$root
+      : this
+    this.$children = []
+    this.$refs = {}       // child vm references
+    this.$els = {}        // element references
+    this._watchers = []   // all watchers as an array
+    this._directives = [] // all directives
+
+    // a uid
+    this._uid = uid++
+
+    // a flag to avoid this being observed
+    this._isVue = true
+
+    // events bookkeeping
+    this._events = {}            // registered callbacks
+    this._eventsCount = {}       // for $broadcast optimization
+
+    // fragment instance properties
+    this._isFragment = false
+    this._fragment =         // @type {DocumentFragment}
+    this._fragmentStart =    // @type {Text|Comment}
+    this._fragmentEnd = null // @type {Text|Comment}
+
+    // lifecycle state
+    this._isCompiled =
+    this._isDestroyed =
+    this._isReady =
+    this._isAttached =
+    this._isBeingDestroyed =
+    this._vForRemoving = false
+    this._unlinkFn = null
+
+    // context:
+    // if this is a transcluded component, context
+    // will be the common parent vm of this instance
+    // and its host.
+    this._context = options._context || this.$parent
+
+    // scope:
+    // if this is inside an inline v-for, the scope
+    // will be the intermediate scope created for this
+    // repeat fragment. this is used for linking props
+    // and container directives.
+    this._scope = options._scope
+
+    // fragment:
+    // if this instance is compiled inside a Fragment, it
+    // needs to register itself as a child of that fragment
+    // for attach/detach to work properly.
+    this._frag = options._frag
+    if (this._frag) {
+      this._frag.children.push(this)
+    }
+
+    // push self into parent / transclusion host
+    if (this.$parent) {
+      this.$parent.$children.push(this)
+    }
+
+    // merge options.
+    options = this.$options = mergeOptions(
+      this.constructor.options,
+      options,
+      this
+    )
+
+    // set ref
+    this._updateRef()
+
+    // initialize data as empty object.
+    // it will be filled up in _initData().
+    this._data = {}
+
+    // call init hook
+    this._callHook('init')
+
+    // initialize data observation and scope inheritance.
+    this._initState()
+
+    // setup event system and option events.
+    this._initEvents()
+
+    // call created hook
+    this._callHook('created')
+
+    // if `el` option is passed, start compilation.
+    if (options.el) {
+      this.$mount(options.el)
+    }
+  }
+}
diff --git a/src/instance/internal/lifecycle.js b/src/instance/internal/lifecycle.js
new file mode 100644
index 00000000000..218d0e3fcef
--- /dev/null
+++ b/src/instance/internal/lifecycle.js
@@ -0,0 +1,272 @@
+import Directive from '../../directive'
+
+import {
+  replace,
+  getAttr,
+  isFragment
+} from '../../util/index'
+
+import {
+  compile,
+  compileRoot,
+  transclude,
+  resolveSlots
+} from '../../compiler/index'
+
+export default function (Vue) {
+  /**
+   * Update v-ref for component.
+   *
+   * @param {Boolean} remove
+   */
+
+  Vue.prototype._updateRef = function (remove) {
+    var ref = this.$options._ref
+    if (ref) {
+      var refs = (this._scope || this._context).$refs
+      if (remove) {
+        if (refs[ref] === this) {
+          refs[ref] = null
+        }
+      } else {
+        refs[ref] = this
+      }
+    }
+  }
+
+  /**
+   * Transclude, compile and link element.
+   *
+   * If a pre-compiled linker is available, that means the
+   * passed in element will be pre-transcluded and compiled
+   * as well - all we need to do is to call the linker.
+   *
+   * Otherwise we need to call transclude/compile/link here.
+   *
+   * @param {Element} el
+   */
+
+  Vue.prototype._compile = function (el) {
+    var options = this.$options
+
+    // transclude and init element
+    // transclude can potentially replace original
+    // so we need to keep reference; this step also injects
+    // the template and caches the original attributes
+    // on the container node and replacer node.
+    var original = el
+    el = transclude(el, options)
+    this._initElement(el)
+
+    // handle v-pre on root node (#2026)
+    if (el.nodeType === 1 && getAttr(el, 'v-pre') !== null) {
+      return
+    }
+
+    // root is always compiled per-instance, because
+    // container attrs and props can be different every time.
+    var contextOptions = this._context && this._context.$options
+    var rootLinker = compileRoot(el, options, contextOptions)
+
+    // resolve slot distribution
+    resolveSlots(this, options._content)
+
+    // compile and link the rest
+    var contentLinkFn
+    var ctor = this.constructor
+    // component compilation can be cached
+    // as long as it's not using inline-template
+    if (options._linkerCachable) {
+      contentLinkFn = ctor.linker
+      if (!contentLinkFn) {
+        contentLinkFn = ctor.linker = compile(el, options)
+      }
+    }
+
+    // link phase
+    // make sure to link root with prop scope!
+    var rootUnlinkFn = rootLinker(this, el, this._scope)
+    var contentUnlinkFn = contentLinkFn
+      ? contentLinkFn(this, el)
+      : compile(el, options)(this, el)
+
+    // register composite unlink function
+    // to be called during instance destruction
+    this._unlinkFn = function () {
+      rootUnlinkFn()
+      // passing destroying: true to avoid searching and
+      // splicing the directives
+      contentUnlinkFn(true)
+    }
+
+    // finally replace original
+    if (options.replace) {
+      replace(original, el)
+    }
+
+    this._isCompiled = true
+    this._callHook('compiled')
+  }
+
+  /**
+   * Initialize instance element. Called in the public
+   * $mount() method.
+   *
+   * @param {Element} el
+   */
+
+  Vue.prototype._initElement = function (el) {
+    if (isFragment(el)) {
+      this._isFragment = true
+      this.$el = this._fragmentStart = el.firstChild
+      this._fragmentEnd = el.lastChild
+      // set persisted text anchors to empty
+      if (this._fragmentStart.nodeType === 3) {
+        this._fragmentStart.data = this._fragmentEnd.data = ''
+      }
+      this._fragment = el
+    } else {
+      this.$el = el
+    }
+    this.$el.__vue__ = this
+    this._callHook('beforeCompile')
+  }
+
+  /**
+   * Create and bind a directive to an element.
+   *
+   * @param {Object} descriptor - parsed directive descriptor
+   * @param {Node} node   - target node
+   * @param {Vue} [host] - transclusion host component
+   * @param {Object} [scope] - v-for scope
+   * @param {Fragment} [frag] - owner fragment
+   */
+
+  Vue.prototype._bindDir = function (descriptor, node, host, scope, frag) {
+    this._directives.push(
+      new Directive(descriptor, this, node, host, scope, frag)
+    )
+  }
+
+  /**
+   * Teardown an instance, unobserves the data, unbind all the
+   * directives, turn off all the event listeners, etc.
+   *
+   * @param {Boolean} remove - whether to remove the DOM node.
+   * @param {Boolean} deferCleanup - if true, defer cleanup to
+   *                                 be called later
+   */
+
+  Vue.prototype._destroy = function (remove, deferCleanup) {
+    if (this._isBeingDestroyed) {
+      if (!deferCleanup) {
+        this._cleanup()
+      }
+      return
+    }
+
+    var destroyReady
+    var pendingRemoval
+
+    var self = this
+    // Cleanup should be called either synchronously or asynchronoysly as
+    // callback of this.$remove(), or if remove and deferCleanup are false.
+    // In any case it should be called after all other removing, unbinding and
+    // turning of is done
+    var cleanupIfPossible = function () {
+      if (destroyReady && !pendingRemoval && !deferCleanup) {
+        self._cleanup()
+      }
+    }
+
+    // remove DOM element
+    if (remove && this.$el) {
+      pendingRemoval = true
+      this.$remove(function () {
+        pendingRemoval = false
+        cleanupIfPossible()
+      })
+    }
+
+    this._callHook('beforeDestroy')
+    this._isBeingDestroyed = true
+    var i
+    // remove self from parent. only necessary
+    // if parent is not being destroyed as well.
+    var parent = this.$parent
+    if (parent && !parent._isBeingDestroyed) {
+      parent.$children.$remove(this)
+      // unregister ref (remove: true)
+      this._updateRef(true)
+    }
+    // destroy all children.
+    i = this.$children.length
+    while (i--) {
+      this.$children[i].$destroy()
+    }
+    // teardown props
+    if (this._propsUnlinkFn) {
+      this._propsUnlinkFn()
+    }
+    // teardown all directives. this also tearsdown all
+    // directive-owned watchers.
+    if (this._unlinkFn) {
+      this._unlinkFn()
+    }
+    i = this._watchers.length
+    while (i--) {
+      this._watchers[i].teardown()
+    }
+    // remove reference to self on $el
+    if (this.$el) {
+      this.$el.__vue__ = null
+    }
+
+    destroyReady = true
+    cleanupIfPossible()
+  }
+
+  /**
+   * Clean up to ensure garbage collection.
+   * This is called after the leave transition if there
+   * is any.
+   */
+
+  Vue.prototype._cleanup = function () {
+    if (this._isDestroyed) {
+      return
+    }
+    // remove self from owner fragment
+    // do it in cleanup so that we can call $destroy with
+    // defer right when a fragment is about to be removed.
+    if (this._frag) {
+      this._frag.children.$remove(this)
+    }
+    // remove reference from data ob
+    // frozen object may not have observer.
+    if (this._data && this._data.__ob__) {
+      this._data.__ob__.removeVm(this)
+    }
+    // Clean up references to private properties and other
+    // instances. preserve reference to _data so that proxy
+    // accessors still work. The only potential side effect
+    // here is that mutating the instance after it's destroyed
+    // may affect the state of other components that are still
+    // observing the same object, but that seems to be a
+    // reasonable responsibility for the user rather than
+    // always throwing an error on them.
+    this.$el =
+    this.$parent =
+    this.$root =
+    this.$children =
+    this._watchers =
+    this._context =
+    this._scope =
+    this._directives = null
+    // call the last hook...
+    this._isDestroyed = true
+    this._callHook('destroyed')
+    // turn off all instance listeners.
+    this.$off()
+  }
+}
diff --git a/src/instance/internal/misc.js b/src/instance/internal/misc.js
new file mode 100644
index 00000000000..578b09324d6
--- /dev/null
+++ b/src/instance/internal/misc.js
@@ -0,0 +1,100 @@
+import {
+  resolveAsset,
+  isPlainObject,
+  warn
+} from '../../util/index'
+
+export default function (Vue) {
+  /**
+   * Apply a list of filter (descriptors) to a value.
+   * Using plain for loops here because this will be called in
+   * the getter of any watcher with filters so it is very
+   * performance sensitive.
+   *
+   * @param {*} value
+   * @param {*} [oldValue]
+   * @param {Array} filters
+   * @param {Boolean} write
+   * @return {*}
+   */
+
+  Vue.prototype._applyFilters = function (value, oldValue, filters, write) {
+    var filter, fn, args, arg, offset, i, l, j, k
+    for (i = 0, l = filters.length; i < l; i++) {
+      filter = filters[write ? l - i - 1 : i]
+      fn = resolveAsset(this.$options, 'filters', filter.name, true)
+      if (!fn) continue
+      fn = write ? fn.write : (fn.read || fn)
+      if (typeof fn !== 'function') continue
+      args = write ? [value, oldValue] : [value]
+      offset = write ? 2 : 1
+      if (filter.args) {
+        for (j = 0, k = filter.args.length; j < k; j++) {
+          arg = filter.args[j]
+          args[j + offset] = arg.dynamic
+            ? this.$get(arg.value)
+            : arg.value
+        }
+      }
+      value = fn.apply(this, args)
+    }
+    return value
+  }
+
+  /**
+   * Resolve a component, depending on whether the component
+   * is defined normally or using an async factory function.
+   * Resolves synchronously if already resolved, otherwise
+   * resolves asynchronously and caches the resolved
+   * constructor on the factory.
+   *
+   * @param {String|Function} value
+   * @param {Function} cb
+   */
+
+  Vue.prototype._resolveComponent = function (value, cb) {
+    var factory
+    if (typeof value === 'function') {
+      factory = value
+    } else {
+      factory = resolveAsset(this.$options, 'components', value, true)
+    }
+    /* istanbul ignore if */
+    if (!factory) {
+      return
+    }
+    // async component factory
+    if (!factory.options) {
+      if (factory.resolved) {
+        // cached
+        cb(factory.resolved)
+      } else if (factory.requested) {
+        // pool callbacks
+        factory.pendingCallbacks.push(cb)
+      } else {
+        factory.requested = true
+        var cbs = factory.pendingCallbacks = [cb]
+        factory.call(this, function resolve (res) {
+          if (isPlainObject(res)) {
+            res = Vue.extend(res)
+          }
+          // cache resolved
+          factory.resolved = res
+          // invoke callbacks
+          for (var i = 0, l = cbs.length; i < l; i++) {
+            cbs[i](res)
+          }
+        }, function reject (reason) {
+          process.env.NODE_ENV !== 'production' && warn(
+            'Failed to resolve async component' +
+            (typeof value === 'string' ? ': ' + value : '') + '. ' +
+            (reason ? '\nReason: ' + reason : '')
+          )
+        })
+      }
+    } else {
+      // normal component
+      cb(factory)
+    }
+  }
+}
diff --git a/src/instance/internal/state.js b/src/instance/internal/state.js
new file mode 100644
index 00000000000..a0ffb7af8a1
--- /dev/null
+++ b/src/instance/internal/state.js
@@ -0,0 +1,276 @@
+import Watcher from '../../watcher'
+import { compileAndLinkProps } from '../../compiler/index'
+import Dep from '../../observer/dep'
+import {
+  observe,
+  defineReactive
+} from '../../observer/index'
+
+import {
+  warn,
+  query,
+  hasOwn,
+  isReserved,
+  isPlainObject,
+  bind
+} from '../../util/index'
+
+export default function (Vue) {
+  /**
+   * Accessor for `$data` property, since setting $data
+   * requires observing the new object and updating
+   * proxied properties.
+   */
+
+  Object.defineProperty(Vue.prototype, '$data', {
+    get () {
+      return this._data
+    },
+    set (newData) {
+      if (newData !== this._data) {
+        this._setData(newData)
+      }
+    }
+  })
+
+  /**
+   * Setup the scope of an instance, which contains:
+   * - observed data
+   * - computed properties
+   * - user methods
+   * - meta properties
+   */
+
+  Vue.prototype._initState = function () {
+    this._initProps()
+    this._initMeta()
+    this._initMethods()
+    this._initData()
+    this._initComputed()
+  }
+
+  /**
+   * Initialize props.
+   */
+
+  Vue.prototype._initProps = function () {
+    var options = this.$options
+    var el = options.el
+    var props = options.props
+    if (props && !el) {
+      process.env.NODE_ENV !== 'production' && warn(
+        'Props will not be compiled if no `el` option is ' +
+        'provided at instantiation.',
+        this
+      )
+    }
+    // make sure to convert string selectors into element now
+    el = options.el = query(el)
+    this._propsUnlinkFn = el && el.nodeType === 1 && props
+      // props must be linked in proper scope if inside v-for
+      ? compileAndLinkProps(this, el, props, this._scope)
+      : null
+  }
+
+  /**
+   * Initialize the data.
+   */
+
+  Vue.prototype._initData = function () {
+    var dataFn = this.$options.data
+    var data = this._data = dataFn ? dataFn() : {}
+    if (!isPlainObject(data)) {
+      data = {}
+      process.env.NODE_ENV !== 'production' && warn(
+        'data functions should return an object.',
+        this
+      )
+    }
+    var props = this._props
+    // proxy data on instance
+    var keys = Object.keys(data)
+    var i, key
+    i = keys.length
+    while (i--) {
+      key = keys[i]
+      // there are two scenarios where we can proxy a data key:
+      // 1. it's not already defined as a prop
+      // 2. it's provided via a instantiation option AND there are no
+      //    template prop present
+      if (!props || !hasOwn(props, key)) {
+        this._proxy(key)
+      } else if (process.env.NODE_ENV !== 'production') {
+        warn(
+          'Data field "' + key + '" is already defined ' +
+          'as a prop. To provide default value for a prop, use the "default" ' +
+          'prop option; if you want to pass prop values to an instantiation ' +
+          'call, use the "propsData" option.',
+          this
+        )
+      }
+    }
+    // observe data
+    observe(data, this)
+  }
+
+  /**
+   * Swap the instance's $data. Called in $data's setter.
+   *
+   * @param {Object} newData
+   */
+
+  Vue.prototype._setData = function (newData) {
+    newData = newData || {}
+    var oldData = this._data
+    this._data = newData
+    var keys, key, i
+    // unproxy keys not present in new data
+    keys = Object.keys(oldData)
+    i = keys.length
+    while (i--) {
+      key = keys[i]
+      if (!(key in newData)) {
+        this._unproxy(key)
+      }
+    }
+    // proxy keys not already proxied,
+    // and trigger change for changed values
+    keys = Object.keys(newData)
+    i = keys.length
+    while (i--) {
+      key = keys[i]
+      if (!hasOwn(this, key)) {
+        // new property
+        this._proxy(key)
+      }
+    }
+    oldData.__ob__.removeVm(this)
+    observe(newData, this)
+    this._digest()
+  }
+
+  /**
+   * Proxy a property, so that
+   * vm.prop === vm._data.prop
+   *
+   * @param {String} key
+   */
+
+  Vue.prototype._proxy = function (key) {
+    if (!isReserved(key)) {
+      // need to store ref to self here
+      // because these getter/setters might
+      // be called by child scopes via
+      // prototype inheritance.
+      var self = this
+      Object.defineProperty(self, key, {
+        configurable: true,
+        enumerable: true,
+        get: function proxyGetter () {
+          return self._data[key]
+        },
+        set: function proxySetter (val) {
+          self._data[key] = val
+        }
+      })
+    }
+  }
+
+  /**
+   * Unproxy a property.
+   *
+   * @param {String} key
+   */
+
+  Vue.prototype._unproxy = function (key) {
+    if (!isReserved(key)) {
+      delete this[key]
+    }
+  }
+
+  /**
+   * Force update on every watcher in scope.
+   */
+
+  Vue.prototype._digest = function () {
+    for (var i = 0, l = this._watchers.length; i < l; i++) {
+      this._watchers[i].update(true) // shallow updates
+    }
+  }
+
+  /**
+   * Setup computed properties. They are essentially
+   * special getter/setters
+   */
+
+  function noop () {}
+  Vue.prototype._initComputed = function () {
+    var computed = this.$options.computed
+    if (computed) {
+      for (var key in computed) {
+        var userDef = computed[key]
+        var def = {
+          enumerable: true,
+          configurable: true
+        }
+        if (typeof userDef === 'function') {
+          def.get = makeComputedGetter(userDef, this)
+          def.set = noop
+        } else {
+          def.get = userDef.get
+            ? userDef.cache !== false
+              ? makeComputedGetter(userDef.get, this)
+              : bind(userDef.get, this)
+            : noop
+          def.set = userDef.set
+            ? bind(userDef.set, this)
+            : noop
+        }
+        Object.defineProperty(this, key, def)
+      }
+    }
+  }
+
+  function makeComputedGetter (getter, owner) {
+    var watcher = new Watcher(owner, getter, null, {
+      lazy: true
+    })
+    return function computedGetter () {
+      if (watcher.dirty) {
+        watcher.evaluate()
+      }
+      if (Dep.target) {
+        watcher.depend()
+      }
+      return watcher.value
+    }
+  }
+
+  /**
+   * Setup instance methods. Methods must be bound to the
+   * instance since they might be passed down as a prop to
+   * child components.
+   */
+
+  Vue.prototype._initMethods = function () {
+    var methods = this.$options.methods
+    if (methods) {
+      for (var key in methods) {
+        this[key] = bind(methods[key], this)
+      }
+    }
+  }
+
+  /**
+   * Initialize meta information like $index, $key & $value.
+   */
+
+  Vue.prototype._initMeta = function () {
+    var metas = this.$options._meta
+    if (metas) {
+      for (var key in metas) {
+        defineReactive(this, key, metas[key])
+      }
+    }
+  }
+}
diff --git a/src/instance/vue.js b/src/instance/vue.js
new file mode 100644
index 00000000000..53b007d018f
--- /dev/null
+++ b/src/instance/vue.js
@@ -0,0 +1,43 @@
+import initMixin from './internal/init'
+import stateMixin from './internal/state'
+import eventsMixin from './internal/events'
+import lifecycleMixin from './internal/lifecycle'
+import miscMixin from './internal/misc'
+
+import dataAPI from './api/data'
+import domAPI from './api/dom'
+import eventsAPI from './api/events'
+import lifecycleAPI from './api/lifecycle'
+
+/**
+ * The exposed Vue constructor.
+ *
+ * API conventions:
+ * - public API methods/properties are prefixed with `$`
+ * - internal methods/properties are prefixed with `_`
+ * - non-prefixed properties are assumed to be proxied user
+ *   data.
+ *
+ * @constructor
+ * @param {Object} [options]
+ * @public
+ */
+
+function Vue (options) {
+  this._init(options)
+}
+
+// install internals
+initMixin(Vue)
+stateMixin(Vue)
+eventsMixin(Vue)
+lifecycleMixin(Vue)
+miscMixin(Vue)
+
+// install instance APIs
+dataAPI(Vue)
+domAPI(Vue)
+eventsAPI(Vue)
+lifecycleAPI(Vue)
+
+export default Vue
diff --git a/src/observer/array.js b/src/observer/array.js
new file mode 100644
index 00000000000..095916fc738
--- /dev/null
+++ b/src/observer/array.js
@@ -0,0 +1,88 @@
+import { def, indexOf } from '../util/index'
+
+const arrayProto = Array.prototype
+export const arrayMethods = Object.create(arrayProto)
+
+/**
+ * Intercept mutating methods and emit events
+ */
+
+;[
+  'push',
+  'pop',
+  'shift',
+  'unshift',
+  'splice',
+  'sort',
+  'reverse'
+]
+.forEach(function (method) {
+  // cache original method
+  var original = arrayProto[method]
+  def(arrayMethods, method, function mutator () {
+    // avoid leaking arguments:
+    // http://jsperf.com/closure-with-arguments
+    var i = arguments.length
+    var args = new Array(i)
+    while (i--) {
+      args[i] = arguments[i]
+    }
+    var result = original.apply(this, args)
+    var ob = this.__ob__
+    var inserted
+    switch (method) {
+      case 'push':
+        inserted = args
+        break
+      case 'unshift':
+        inserted = args
+        break
+      case 'splice':
+        inserted = args.slice(2)
+        break
+    }
+    if (inserted) ob.observeArray(inserted)
+    // notify change
+    ob.dep.notify()
+    return result
+  })
+})
+
+/**
+ * Swap the element at the given index with a new value
+ * and emits corresponding event.
+ *
+ * @param {Number} index
+ * @param {*} val
+ * @return {*} - replaced element
+ */
+
+def(
+  arrayProto,
+  '$set',
+  function $set (index, val) {
+    if (index >= this.length) {
+      this.length = Number(index) + 1
+    }
+    return this.splice(index, 1, val)[0]
+  }
+)
+
+/**
+ * Convenience method to remove the element at given index or target element reference.
+ *
+ * @param {*} item
+ */
+
+def(
+  arrayProto,
+  '$remove',
+  function $remove (item) {
+    /* istanbul ignore if */
+    if (!this.length) return
+    var index = indexOf(this, item)
+    if (index > -1) {
+      return this.splice(index, 1)
+    }
+  }
+)
diff --git a/src/observer/dep.js b/src/observer/dep.js
new file mode 100644
index 00000000000..d0c5d00617b
--- /dev/null
+++ b/src/observer/dep.js
@@ -0,0 +1,60 @@
+import { toArray } from '../util/index'
+
+let uid = 0
+
+/**
+ * A dep is an observable that can have multiple
+ * directives subscribing to it.
+ *
+ * @constructor
+ */
+
+export default function Dep () {
+  this.id = uid++
+  this.subs = []
+}
+
+// the current target watcher being evaluated.
+// this is globally unique because there could be only one
+// watcher being evaluated at any time.
+Dep.target = null
+
+/**
+ * Add a directive subscriber.
+ *
+ * @param {Directive} sub
+ */
+
+Dep.prototype.addSub = function (sub) {
+  this.subs.push(sub)
+}
+
+/**
+ * Remove a directive subscriber.
+ *
+ * @param {Directive} sub
+ */
+
+Dep.prototype.removeSub = function (sub) {
+  this.subs.$remove(sub)
+}
+
+/**
+ * Add self as a dependency to the target watcher.
+ */
+
+Dep.prototype.depend = function () {
+  Dep.target.addDep(this)
+}
+
+/**
+ * Notify all subscribers of a new value.
+ */
+
+Dep.prototype.notify = function () {
+  // stablize the subscriber list first
+  var subs = toArray(this.subs)
+  for (var i = 0, l = subs.length; i < l; i++) {
+    subs[i].update()
+  }
+}
diff --git a/src/observer/index.js b/src/observer/index.js
new file mode 100644
index 00000000000..a1198ccbfa1
--- /dev/null
+++ b/src/observer/index.js
@@ -0,0 +1,240 @@
+import Dep from './dep'
+import { arrayMethods } from './array'
+import {
+  def,
+  isArray,
+  isPlainObject,
+  hasProto,
+  hasOwn
+} from '../util/index'
+
+const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
+
+/**
+ * By default, when a reactive property is set, the new value is
+ * also converted to become reactive. However in certain cases, e.g.
+ * v-for scope alias and props, we don't want to force conversion
+ * because the value may be a nested value under a frozen data structure.
+ *
+ * So whenever we want to set a reactive property without forcing
+ * conversion on the new value, we wrap that call inside this function.
+ */
+
+let shouldConvert = true
+export function withoutConversion (fn) {
+  shouldConvert = false
+  fn()
+  shouldConvert = true
+}
+
+/**
+ * Observer class that are attached to each observed
+ * object. Once attached, the observer converts target
+ * object's property keys into getter/setters that
+ * collect dependencies and dispatches updates.
+ *
+ * @param {Array|Object} value
+ * @constructor
+ */
+
+export function Observer (value) {
+  this.value = value
+  this.dep = new Dep()
+  def(value, '__ob__', this)
+  if (isArray(value)) {
+    var augment = hasProto
+      ? protoAugment
+      : copyAugment
+    augment(value, arrayMethods, arrayKeys)
+    this.observeArray(value)
+  } else {
+    this.walk(value)
+  }
+}
+
+// Instance methods
+
+/**
+ * Walk through each property and convert them into
+ * getter/setters. This method should only be called when
+ * value type is Object.
+ *
+ * @param {Object} obj
+ */
+
+Observer.prototype.walk = function (obj) {
+  var keys = Object.keys(obj)
+  for (var i = 0, l = keys.length; i < l; i++) {
+    this.convert(keys[i], obj[keys[i]])
+  }
+}
+
+/**
+ * Observe a list of Array items.
+ *
+ * @param {Array} items
+ */
+
+Observer.prototype.observeArray = function (items) {
+  for (var i = 0, l = items.length; i < l; i++) {
+    observe(items[i])
+  }
+}
+
+/**
+ * Convert a property into getter/setter so we can emit
+ * the events when the property is accessed/changed.
+ *
+ * @param {String} key
+ * @param {*} val
+ */
+
+Observer.prototype.convert = function (key, val) {
+  defineReactive(this.value, key, val)
+}
+
+/**
+ * Add an owner vm, so that when $set/$delete mutations
+ * happen we can notify owner vms to proxy the keys and
+ * digest the watchers. This is only called when the object
+ * is observed as an instance's root $data.
+ *
+ * @param {Vue} vm
+ */
+
+Observer.prototype.addVm = function (vm) {
+  (this.vms || (this.vms = [])).push(vm)
+}
+
+/**
+ * Remove an owner vm. This is called when the object is
+ * swapped out as an instance's $data object.
+ *
+ * @param {Vue} vm
+ */
+
+Observer.prototype.removeVm = function (vm) {
+  this.vms.$remove(vm)
+}
+
+// helpers
+
+/**
+ * Augment an target Object or Array by intercepting
+ * the prototype chain using __proto__
+ *
+ * @param {Object|Array} target
+ * @param {Object} src
+ */
+
+function protoAugment (target, src) {
+  /* eslint-disable no-proto */
+  target.__proto__ = src
+  /* eslint-enable no-proto */
+}
+
+/**
+ * Augment an target Object or Array by defining
+ * hidden properties.
+ *
+ * @param {Object|Array} target
+ * @param {Object} proto
+ */
+
+function copyAugment (target, src, keys) {
+  for (var i = 0, l = keys.length; i < l; i++) {
+    var key = keys[i]
+    def(target, key, src[key])
+  }
+}
+
+/**
+ * Attempt to create an observer instance for a value,
+ * returns the new observer if successfully observed,
+ * or the existing observer if the value already has one.
+ *
+ * @param {*} value
+ * @param {Vue} [vm]
+ * @return {Observer|undefined}
+ * @static
+ */
+
+export function observe (value, vm) {
+  if (!value || typeof value !== 'object') {
+    return
+  }
+  var ob
+  if (
+    hasOwn(value, '__ob__') &&
+    value.__ob__ instanceof Observer
+  ) {
+    ob = value.__ob__
+  } else if (
+    shouldConvert &&
+    (isArray(value) || isPlainObject(value)) &&
+    Object.isExtensible(value) &&
+    !value._isVue
+  ) {
+    ob = new Observer(value)
+  }
+  if (ob && vm) {
+    ob.addVm(vm)
+  }
+  return ob
+}
+
+/**
+ * Define a reactive property on an Object.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {*} val
+ */
+
+export function defineReactive (obj, key, val) {
+  var dep = new Dep()
+
+  var property = Object.getOwnPropertyDescriptor(obj, key)
+  if (property && property.configurable === false) {
+    return
+  }
+
+  // cater for pre-defined getter/setters
+  var getter = property && property.get
+  var setter = property && property.set
+
+  var childOb = observe(val)
+  Object.defineProperty(obj, key, {
+    enumerable: true,
+    configurable: true,
+    get: function reactiveGetter () {
+      var value = getter ? getter.call(obj) : val
+      if (Dep.target) {
+        dep.depend()
+        if (childOb) {
+          childOb.dep.depend()
+        }
+        if (isArray(value)) {
+          for (var e, i = 0, l = value.length; i < l; i++) {
+            e = value[i]
+            e && e.__ob__ && e.__ob__.dep.depend()
+          }
+        }
+      }
+      return value
+    },
+    set: function reactiveSetter (newVal) {
+      var value = getter ? getter.call(obj) : val
+      if (newVal === value) {
+        return
+      }
+      if (setter) {
+        setter.call(obj, newVal)
+      } else {
+        val = newVal
+      }
+      childOb = observe(newVal)
+      dep.notify()
+    }
+  })
+}
diff --git a/src/parsers/directive.js b/src/parsers/directive.js
new file mode 100644
index 00000000000..0ee9eea56b7
--- /dev/null
+++ b/src/parsers/directive.js
@@ -0,0 +1,243 @@
+import { toNumber, stripQuotes } from '../util/index'
+import Cache from '../cache'
+
+const cache = new Cache(1000)
+const reservedArgRE = /^in$|^-?\d+/
+
+/**
+ * Parser state
+ */
+
+var str, dir, len
+var index
+var chr
+var state
+var startState = 0
+var filterState = 1
+var filterNameState = 2
+var filterArgState = 3
+
+var doubleChr = 0x22
+var singleChr = 0x27
+var pipeChr = 0x7C
+var escapeChr = 0x5C
+var spaceChr = 0x20
+
+var expStartChr = { 0x5B: 1, 0x7B: 1, 0x28: 1 }
+var expChrPair = { 0x5B: 0x5D, 0x7B: 0x7D, 0x28: 0x29 }
+
+function peek () {
+  return str.charCodeAt(index + 1)
+}
+
+function next () {
+  return str.charCodeAt(++index)
+}
+
+function eof () {
+  return index >= len
+}
+
+function eatSpace () {
+  while (peek() === spaceChr) {
+    next()
+  }
+}
+
+function isStringStart (chr) {
+  return chr === doubleChr || chr === singleChr
+}
+
+function isExpStart (chr) {
+  return expStartChr[chr]
+}
+
+function isExpEnd (start, chr) {
+  return expChrPair[start] === chr
+}
+
+function parseString () {
+  var stringQuote = next()
+  var chr
+  while (!eof()) {
+    chr = next()
+    // escape char
+    if (chr === escapeChr) {
+      next()
+    } else if (chr === stringQuote) {
+      break
+    }
+  }
+}
+
+function parseSpecialExp (chr) {
+  var inExp = 0
+  var startChr = chr
+
+  while (!eof()) {
+    chr = peek()
+    if (isStringStart(chr)) {
+      parseString()
+      continue
+    }
+
+    if (startChr === chr) {
+      inExp++
+    }
+    if (isExpEnd(startChr, chr)) {
+      inExp--
+    }
+
+    next()
+
+    if (inExp === 0) {
+      break
+    }
+  }
+}
+
+/**
+ * syntax:
+ * expression | filterName  [arg  arg [| filterName arg arg]]
+ */
+
+function parseExpression () {
+  var start = index
+  while (!eof()) {
+    chr = peek()
+    if (isStringStart(chr)) {
+      parseString()
+    } else if (isExpStart(chr)) {
+      parseSpecialExp(chr)
+    } else if (chr === pipeChr) {
+      next()
+      chr = peek()
+      if (chr === pipeChr) {
+        next()
+      } else {
+        if (state === startState || state === filterArgState) {
+          state = filterState
+        }
+        break
+      }
+    } else if (chr === spaceChr && (state === filterNameState || state === filterArgState)) {
+      eatSpace()
+      break
+    } else {
+      if (state === filterState) {
+        state = filterNameState
+      }
+      next()
+    }
+  }
+
+  return str.slice(start + 1, index) || null
+}
+
+function parseFilterList () {
+  var filters = []
+  while (!eof()) {
+    filters.push(parseFilter())
+  }
+  return filters
+}
+
+function parseFilter () {
+  var filter = {}
+  var args
+
+  state = filterState
+  filter.name = parseExpression().trim()
+
+  state = filterArgState
+  args = parseFilterArguments()
+
+  if (args.length) {
+    filter.args = args
+  }
+  return filter
+}
+
+function parseFilterArguments () {
+  var args = []
+  while (!eof() && state !== filterState) {
+    var arg = parseExpression()
+    if (!arg) {
+      break
+    }
+    args.push(processFilterArg(arg))
+  }
+
+  return args
+}
+
+/**
+ * Check if an argument is dynamic and strip quotes.
+ *
+ * @param {String} arg
+ * @return {Object}
+ */
+
+function processFilterArg (arg) {
+  if (reservedArgRE.test(arg)) {
+    return {
+      value: toNumber(arg),
+      dynamic: false
+    }
+  } else {
+    var stripped = stripQuotes(arg)
+    var dynamic = stripped === arg
+    return {
+      value: dynamic ? arg : stripped,
+      dynamic: dynamic
+    }
+  }
+}
+
+/**
+ * Parse a directive value and extract the expression
+ * and its filters into a descriptor.
+ *
+ * Example:
+ *
+ * "a + 1 | uppercase" will yield:
+ * {
+ *   expression: 'a + 1',
+ *   filters: [
+ *     { name: 'uppercase', args: null }
+ *   ]
+ * }
+ *
+ * @param {String} s
+ * @return {Object}
+ */
+
+export function parseDirective (s) {
+  var hit = cache.get(s)
+  if (hit) {
+    return hit
+  }
+
+  // reset parser state
+  str = s
+  dir = {}
+  len = str.length
+  index = -1
+  chr = ''
+  state = startState
+
+  var filters
+
+  if (str.indexOf('|') < 0) {
+    dir.expression = str.trim()
+  } else {
+    dir.expression = parseExpression().trim()
+    filters = parseFilterList()
+    if (filters.length) {
+      dir.filters = filters
+    }
+  }
+
+  cache.put(s, dir)
+  return dir
+}
diff --git a/src/parsers/expression.js b/src/parsers/expression.js
new file mode 100644
index 00000000000..b0d962d6d38
--- /dev/null
+++ b/src/parsers/expression.js
@@ -0,0 +1,229 @@
+import { warn } from '../util/index'
+import { parsePath, setPath } from './path'
+import Cache from '../cache'
+
+const expressionCache = new Cache(1000)
+
+const allowedKeywords =
+  'Math,Date,this,true,false,null,undefined,Infinity,NaN,' +
+  'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,' +
+  'encodeURIComponent,parseInt,parseFloat'
+const allowedKeywordsRE =
+  new RegExp('^(' + allowedKeywords.replace(/,/g, '\\b|') + '\\b)')
+
+// keywords that don't make sense inside expressions
+const improperKeywords =
+  'break,case,class,catch,const,continue,debugger,default,' +
+  'delete,do,else,export,extends,finally,for,function,if,' +
+  'import,in,instanceof,let,return,super,switch,throw,try,' +
+  'var,while,with,yield,enum,await,implements,package,' +
+  'protected,static,interface,private,public'
+const improperKeywordsRE =
+  new RegExp('^(' + improperKeywords.replace(/,/g, '\\b|') + '\\b)')
+
+const wsRE = /\s/g
+const newlineRE = /\n/g
+const saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\"']|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g
+const restoreRE = /"(\d+)"/g
+const pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/
+const identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g
+const literalValueRE = /^(?:true|false|null|undefined|Infinity|NaN)$/
+
+function noop () {}
+
+/**
+ * Save / Rewrite / Restore
+ *
+ * When rewriting paths found in an expression, it is
+ * possible for the same letter sequences to be found in
+ * strings and Object literal property keys. Therefore we
+ * remove and store these parts in a temporary array, and
+ * restore them after the path rewrite.
+ */
+
+var saved = []
+
+/**
+ * Save replacer
+ *
+ * The save regex can match two possible cases:
+ * 1. An opening object literal
+ * 2. A string
+ * If matched as a plain string, we need to escape its
+ * newlines, since the string needs to be preserved when
+ * generating the function body.
+ *
+ * @param {String} str
+ * @param {String} isString - str if matched as a string
+ * @return {String} - placeholder with index
+ */
+
+function save (str, isString) {
+  var i = saved.length
+  saved[i] = isString
+    ? str.replace(newlineRE, '\\n')
+    : str
+  return '"' + i + '"'
+}
+
+/**
+ * Path rewrite replacer
+ *
+ * @param {String} raw
+ * @return {String}
+ */
+
+function rewrite (raw) {
+  var c = raw.charAt(0)
+  var path = raw.slice(1)
+  if (allowedKeywordsRE.test(path)) {
+    return raw
+  } else {
+    path = path.indexOf('"') > -1
+      ? path.replace(restoreRE, restore)
+      : path
+    return c + 'scope.' + path
+  }
+}
+
+/**
+ * Restore replacer
+ *
+ * @param {String} str
+ * @param {String} i - matched save index
+ * @return {String}
+ */
+
+function restore (str, i) {
+  return saved[i]
+}
+
+/**
+ * Rewrite an expression, prefixing all path accessors with
+ * `scope.` and generate getter/setter functions.
+ *
+ * @param {String} exp
+ * @return {Function}
+ */
+
+function compileGetter (exp) {
+  if (improperKeywordsRE.test(exp)) {
+    process.env.NODE_ENV !== 'production' && warn(
+      'Avoid using reserved keywords in expression: ' + exp
+    )
+  }
+  // reset state
+  saved.length = 0
+  // save strings and object literal keys
+  var body = exp
+    .replace(saveRE, save)
+    .replace(wsRE, '')
+  // rewrite all paths
+  // pad 1 space here because the regex matches 1 extra char
+  body = (' ' + body)
+    .replace(identRE, rewrite)
+    .replace(restoreRE, restore)
+  return makeGetterFn(body)
+}
+
+/**
+ * Build a getter function. Requires eval.
+ *
+ * We isolate the try/catch so it doesn't affect the
+ * optimization of the parse function when it is not called.
+ *
+ * @param {String} body
+ * @return {Function|undefined}
+ */
+
+function makeGetterFn (body) {
+  try {
+    /* eslint-disable no-new-func */
+    return new Function('scope', 'return ' + body + ';')
+    /* eslint-enable no-new-func */
+  } catch (e) {
+    if (process.env.NODE_ENV !== 'production') {
+      /* istanbul ignore if */
+      if (e.toString().match(/unsafe-eval|CSP/)) {
+        warn(
+          'It seems you are using the default build of Vue.js in an environment ' +
+          'with Content Security Policy that prohibits unsafe-eval. ' +
+          'Use the CSP-compliant build instead: ' +
+          'http://vuejs.org/guide/installation.html#CSP-compliant-build'
+        )
+      } else {
+        warn(
+          'Invalid expression. ' +
+          'Generated function body: ' + body
+        )
+      }
+    }
+    return noop
+  }
+}
+
+/**
+ * Compile a setter function for the expression.
+ *
+ * @param {String} exp
+ * @return {Function|undefined}
+ */
+
+function compileSetter (exp) {
+  var path = parsePath(exp)
+  if (path) {
+    return function (scope, val) {
+      setPath(scope, path, val)
+    }
+  } else {
+    process.env.NODE_ENV !== 'production' && warn(
+      'Invalid setter expression: ' + exp
+    )
+  }
+}
+
+/**
+ * Parse an expression into re-written getter/setters.
+ *
+ * @param {String} exp
+ * @param {Boolean} needSet
+ * @return {Function}
+ */
+
+export function parseExpression (exp, needSet) {
+  exp = exp.trim()
+  // try cache
+  var hit = expressionCache.get(exp)
+  if (hit) {
+    if (needSet && !hit.set) {
+      hit.set = compileSetter(hit.exp)
+    }
+    return hit
+  }
+  var res = { exp: exp }
+  res.get = isSimplePath(exp) && exp.indexOf('[') < 0
+    // optimized super simple getter
+    ? makeGetterFn('scope.' + exp)
+    // dynamic getter
+    : compileGetter(exp)
+  if (needSet) {
+    res.set = compileSetter(exp)
+  }
+  expressionCache.put(exp, res)
+  return res
+}
+
+/**
+ * Check if an expression is a simple path.
+ *
+ * @param {String} exp
+ * @return {Boolean}
+ */
+
+export function isSimplePath (exp) {
+  return pathTestRE.test(exp) &&
+    // don't treat literal values as paths
+    !literalValueRE.test(exp) &&
+    // Math constants e.g. Math.PI, Math.E etc.
+    exp.slice(0, 5) !== 'Math.'
+}
diff --git a/src/parsers/path.js b/src/parsers/path.js
new file mode 100644
index 00000000000..cfef1493acd
--- /dev/null
+++ b/src/parsers/path.js
@@ -0,0 +1,347 @@
+import { parseExpression } from './expression'
+import {
+  isLiteral,
+  stripQuotes,
+  isObject,
+  isArray,
+  warn,
+  set
+} from '../util/index'
+import Cache from '../cache'
+
+var pathCache = new Cache(1000)
+
+// actions
+var APPEND = 0
+var PUSH = 1
+var INC_SUB_PATH_DEPTH = 2
+var PUSH_SUB_PATH = 3
+
+// states
+var BEFORE_PATH = 0
+var IN_PATH = 1
+var BEFORE_IDENT = 2
+var IN_IDENT = 3
+var IN_SUB_PATH = 4
+var IN_SINGLE_QUOTE = 5
+var IN_DOUBLE_QUOTE = 6
+var AFTER_PATH = 7
+var ERROR = 8
+
+var pathStateMachine = []
+
+pathStateMachine[BEFORE_PATH] = {
+  'ws': [BEFORE_PATH],
+  'ident': [IN_IDENT, APPEND],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+}
+
+pathStateMachine[IN_PATH] = {
+  'ws': [IN_PATH],
+  '.': [BEFORE_IDENT],
+  '[': [IN_SUB_PATH],
+  'eof': [AFTER_PATH]
+}
+
+pathStateMachine[BEFORE_IDENT] = {
+  'ws': [BEFORE_IDENT],
+  'ident': [IN_IDENT, APPEND]
+}
+
+pathStateMachine[IN_IDENT] = {
+  'ident': [IN_IDENT, APPEND],
+  '0': [IN_IDENT, APPEND],
+  'number': [IN_IDENT, APPEND],
+  'ws': [IN_PATH, PUSH],
+  '.': [BEFORE_IDENT, PUSH],
+  '[': [IN_SUB_PATH, PUSH],
+  'eof': [AFTER_PATH, PUSH]
+}
+
+pathStateMachine[IN_SUB_PATH] = {
+  "'": [IN_SINGLE_QUOTE, APPEND],
+  '"': [IN_DOUBLE_QUOTE, APPEND],
+  '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
+  ']': [IN_PATH, PUSH_SUB_PATH],
+  'eof': ERROR,
+  'else': [IN_SUB_PATH, APPEND]
+}
+
+pathStateMachine[IN_SINGLE_QUOTE] = {
+  "'": [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_SINGLE_QUOTE, APPEND]
+}
+
+pathStateMachine[IN_DOUBLE_QUOTE] = {
+  '"': [IN_SUB_PATH, APPEND],
+  'eof': ERROR,
+  'else': [IN_DOUBLE_QUOTE, APPEND]
+}
+
+/**
+ * Determine the type of a character in a keypath.
+ *
+ * @param {Char} ch
+ * @return {String} type
+ */
+
+function getPathCharType (ch) {
+  if (ch === undefined) {
+    return 'eof'
+  }
+
+  var code = ch.charCodeAt(0)
+
+  switch (code) {
+    case 0x5B: // [
+    case 0x5D: // ]
+    case 0x2E: // .
+    case 0x22: // "
+    case 0x27: // '
+    case 0x30: // 0
+      return ch
+
+    case 0x5F: // _
+    case 0x24: // $
+      return 'ident'
+
+    case 0x20: // Space
+    case 0x09: // Tab
+    case 0x0A: // Newline
+    case 0x0D: // Return
+    case 0xA0:  // No-break space
+    case 0xFEFF:  // Byte Order Mark
+    case 0x2028:  // Line Separator
+    case 0x2029:  // Paragraph Separator
+      return 'ws'
+  }
+
+  // a-z, A-Z
+  if (
+    (code >= 0x61 && code <= 0x7A) ||
+    (code >= 0x41 && code <= 0x5A)
+  ) {
+    return 'ident'
+  }
+
+  // 1-9
+  if (code >= 0x31 && code <= 0x39) {
+    return 'number'
+  }
+
+  return 'else'
+}
+
+/**
+ * Format a subPath, return its plain form if it is
+ * a literal string or number. Otherwise prepend the
+ * dynamic indicator (*).
+ *
+ * @param {String} path
+ * @return {String}
+ */
+
+function formatSubPath (path) {
+  var trimmed = path.trim()
+  // invalid leading 0
+  if (path.charAt(0) === '0' && isNaN(path)) {
+    return false
+  }
+  return isLiteral(trimmed)
+    ? stripQuotes(trimmed)
+    : '*' + trimmed
+}
+
+/**
+ * Parse a string path into an array of segments
+ *
+ * @param {String} path
+ * @return {Array|undefined}
+ */
+
+function parse (path) {
+  var keys = []
+  var index = -1
+  var mode = BEFORE_PATH
+  var subPathDepth = 0
+  var c, newChar, key, type, transition, action, typeMap
+
+  var actions = []
+
+  actions[PUSH] = function () {
+    if (key !== undefined) {
+      keys.push(key)
+      key = undefined
+    }
+  }
+
+  actions[APPEND] = function () {
+    if (key === undefined) {
+      key = newChar
+    } else {
+      key += newChar
+    }
+  }
+
+  actions[INC_SUB_PATH_DEPTH] = function () {
+    actions[APPEND]()
+    subPathDepth++
+  }
+
+  actions[PUSH_SUB_PATH] = function () {
+    if (subPathDepth > 0) {
+      subPathDepth--
+      mode = IN_SUB_PATH
+      actions[APPEND]()
+    } else {
+      subPathDepth = 0
+      key = formatSubPath(key)
+      if (key === false) {
+        return false
+      } else {
+        actions[PUSH]()
+      }
+    }
+  }
+
+  function maybeUnescapeQuote () {
+    var nextChar = path[index + 1]
+    if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
+        (mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
+      index++
+      newChar = '\\' + nextChar
+      actions[APPEND]()
+      return true
+    }
+  }
+
+  while (mode != null) {
+    index++
+    c = path[index]
+
+    if (c === '\\' && maybeUnescapeQuote()) {
+      continue
+    }
+
+    type = getPathCharType(c)
+    typeMap = pathStateMachine[mode]
+    transition = typeMap[type] || typeMap['else'] || ERROR
+
+    if (transition === ERROR) {
+      return // parse error
+    }
+
+    mode = transition[0]
+    action = actions[transition[1]]
+    if (action) {
+      newChar = transition[2]
+      newChar = newChar === undefined
+        ? c
+        : newChar
+      if (action() === false) {
+        return
+      }
+    }
+
+    if (mode === AFTER_PATH) {
+      keys.raw = path
+      return keys
+    }
+  }
+}
+
+/**
+ * External parse that check for a cache hit first
+ *
+ * @param {String} path
+ * @return {Array|undefined}
+ */
+
+export function parsePath (path) {
+  var hit = pathCache.get(path)
+  if (!hit) {
+    hit = parse(path)
+    if (hit) {
+      pathCache.put(path, hit)
+    }
+  }
+  return hit
+}
+
+/**
+ * Get from an object from a path string
+ *
+ * @param {Object} obj
+ * @param {String} path
+ */
+
+export function getPath (obj, path) {
+  return parseExpression(path).get(obj)
+}
+
+/**
+ * Warn against setting non-existent root path on a vm.
+ */
+
+var warnNonExistent
+if (process.env.NODE_ENV !== 'production') {
+  warnNonExistent = function (path, vm) {
+    warn(
+      'You are setting a non-existent path "' + path.raw + '" ' +
+      'on a vm instance. Consider pre-initializing the property ' +
+      'with the "data" option for more reliable reactivity ' +
+      'and better performance.',
+      vm
+    )
+  }
+}
+
+/**
+ * Set on an object from a path
+ *
+ * @param {Object} obj
+ * @param {String | Array} path
+ * @param {*} val
+ */
+
+export function setPath (obj, path, val) {
+  var original = obj
+  if (typeof path === 'string') {
+    path = parse(path)
+  }
+  if (!path || !isObject(obj)) {
+    return false
+  }
+  var last, key
+  for (var i = 0, l = path.length; i < l; i++) {
+    last = obj
+    key = path[i]
+    if (key.charAt(0) === '*') {
+      key = parseExpression(key.slice(1)).get.call(original, original)
+    }
+    if (i < l - 1) {
+      obj = obj[key]
+      if (!isObject(obj)) {
+        obj = {}
+        if (process.env.NODE_ENV !== 'production' && last._isVue) {
+          warnNonExistent(path, last)
+        }
+        set(last, key, obj)
+      }
+    } else {
+      if (isArray(obj)) {
+        obj.$set(key, val)
+      } else if (key in obj) {
+        obj[key] = val
+      } else {
+        if (process.env.NODE_ENV !== 'production' && obj._isVue) {
+          warnNonExistent(path, obj)
+        }
+        set(obj, key, val)
+      }
+    }
+  }
+  return true
+}
diff --git a/src/parsers/template.js b/src/parsers/template.js
new file mode 100644
index 00000000000..5be9158179e
--- /dev/null
+++ b/src/parsers/template.js
@@ -0,0 +1,306 @@
+import Cache from '../cache'
+import {
+  inBrowser,
+  trimNode,
+  isTemplate,
+  isFragment
+} from '../util/index'
+
+const templateCache = new Cache(1000)
+const idSelectorCache = new Cache(1000)
+
+const map = {
+  efault: [0, '', ''],
+  legend: [1, '<fieldset>', '</fieldset>'],
+  tr: [2, '<table><tbody>', '</tbody></table>'],
+  col: [
+    2,
+    '<table><tbody></tbody><colgroup>',
+    '</colgroup></table>'
+  ]
+}
+
+map.td =
+map.th = [
+  3,
+  '<table><tbody><tr>',
+  '</tr></tbody></table>'
+]
+
+map.option =
+map.optgroup = [
+  1,
+  '<select multiple="multiple">',
+  '</select>'
+]
+
+map.thead =
+map.tbody =
+map.colgroup =
+map.caption =
+map.tfoot = [1, '<table>', '</table>']
+
+map.g =
+map.defs =
+map.symbol =
+map.use =
+map.image =
+map.text =
+map.circle =
+map.ellipse =
+map.line =
+map.path =
+map.polygon =
+map.polyline =
+map.rect = [
+  1,
+  '<svg ' +
+    'xmlns="http://www.w3.org/2000/svg" ' +
+    'xmlns:xlink="http://www.w3.org/1999/xlink" ' +
+    'xmlns:ev="http://www.w3.org/2001/xml-events"' +
+    'version="1.1">',
+  '</svg>'
+]
+
+/**
+ * Check if a node is a supported template node with a
+ * DocumentFragment content.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+function isRealTemplate (node) {
+  return isTemplate(node) && isFragment(node.content)
+}
+
+const tagRE = /<([\w:-]+)/
+const entityRE = /&#?\w+?;/
+const commentRE = /<!--/
+
+/**
+ * Convert a string template to a DocumentFragment.
+ * Determines correct wrapping by tag types. Wrapping
+ * strategy found in jQuery & component/domify.
+ *
+ * @param {String} templateString
+ * @param {Boolean} raw
+ * @return {DocumentFragment}
+ */
+
+function stringToFragment (templateString, raw) {
+  // try a cache hit first
+  var cacheKey = raw
+    ? templateString
+    : templateString.trim()
+  var hit = templateCache.get(cacheKey)
+  if (hit) {
+    return hit
+  }
+
+  var frag = document.createDocumentFragment()
+  var tagMatch = templateString.match(tagRE)
+  var entityMatch = entityRE.test(templateString)
+  var commentMatch = commentRE.test(templateString)
+
+  if (!tagMatch && !entityMatch && !commentMatch) {
+    // text only, return a single text node.
+    frag.appendChild(
+      document.createTextNode(templateString)
+    )
+  } else {
+    var tag = tagMatch && tagMatch[1]
+    var wrap = map[tag] || map.efault
+    var depth = wrap[0]
+    var prefix = wrap[1]
+    var suffix = wrap[2]
+    var node = document.createElement('div')
+
+    node.innerHTML = prefix + templateString + suffix
+    while (depth--) {
+      node = node.lastChild
+    }
+
+    var child
+    /* eslint-disable no-cond-assign */
+    while (child = node.firstChild) {
+    /* eslint-enable no-cond-assign */
+      frag.appendChild(child)
+    }
+  }
+  if (!raw) {
+    trimNode(frag)
+  }
+  templateCache.put(cacheKey, frag)
+  return frag
+}
+
+/**
+ * Convert a template node to a DocumentFragment.
+ *
+ * @param {Node} node
+ * @return {DocumentFragment}
+ */
+
+function nodeToFragment (node) {
+  // if its a template tag and the browser supports it,
+  // its content is already a document fragment. However, iOS Safari has
+  // bug when using directly cloned template content with touch
+  // events and can cause crashes when the nodes are removed from DOM, so we
+  // have to treat template elements as string templates. (#2805)
+  /* istanbul ignore if */
+  if (isRealTemplate(node)) {
+    return stringToFragment(node.innerHTML)
+  }
+  // script template
+  if (node.tagName === 'SCRIPT') {
+    return stringToFragment(node.textContent)
+  }
+  // normal node, clone it to avoid mutating the original
+  var clonedNode = cloneNode(node)
+  var frag = document.createDocumentFragment()
+  var child
+  /* eslint-disable no-cond-assign */
+  while (child = clonedNode.firstChild) {
+  /* eslint-enable no-cond-assign */
+    frag.appendChild(child)
+  }
+  trimNode(frag)
+  return frag
+}
+
+// Test for the presence of the Safari template cloning bug
+// https://bugs.webkit.org/showug.cgi?id=137755
+var hasBrokenTemplate = (function () {
+  /* istanbul ignore else */
+  if (inBrowser) {
+    var a = document.createElement('div')
+    a.innerHTML = '<template>1</template>'
+    return !a.cloneNode(true).firstChild.innerHTML
+  } else {
+    return false
+  }
+})()
+
+// Test for IE10/11 textarea placeholder clone bug
+var hasTextareaCloneBug = (function () {
+  /* istanbul ignore else */
+  if (inBrowser) {
+    var t = document.createElement('textarea')
+    t.placeholder = 't'
+    return t.cloneNode(true).value === 't'
+  } else {
+    return false
+  }
+})()
+
+/**
+ * 1. Deal with Safari cloning nested <template> bug by
+ *    manually cloning all template instances.
+ * 2. Deal with IE10/11 textarea placeholder bug by setting
+ *    the correct value after cloning.
+ *
+ * @param {Element|DocumentFragment} node
+ * @return {Element|DocumentFragment}
+ */
+
+export function cloneNode (node) {
+  /* istanbul ignore if */
+  if (!node.querySelectorAll) {
+    return node.cloneNode()
+  }
+  var res = node.cloneNode(true)
+  var i, original, cloned
+  /* istanbul ignore if */
+  if (hasBrokenTemplate) {
+    var tempClone = res
+    if (isRealTemplate(node)) {
+      node = node.content
+      tempClone = res.content
+    }
+    original = node.querySelectorAll('template')
+    if (original.length) {
+      cloned = tempClone.querySelectorAll('template')
+      i = cloned.length
+      while (i--) {
+        cloned[i].parentNode.replaceChild(
+          cloneNode(original[i]),
+          cloned[i]
+        )
+      }
+    }
+  }
+  /* istanbul ignore if */
+  if (hasTextareaCloneBug) {
+    if (node.tagName === 'TEXTAREA') {
+      res.value = node.value
+    } else {
+      original = node.querySelectorAll('textarea')
+      if (original.length) {
+        cloned = res.querySelectorAll('textarea')
+        i = cloned.length
+        while (i--) {
+          cloned[i].value = original[i].value
+        }
+      }
+    }
+  }
+  return res
+}
+
+/**
+ * Process the template option and normalizes it into a
+ * a DocumentFragment that can be used as a partial or a
+ * instance template.
+ *
+ * @param {*} template
+ *        Possible values include:
+ *        - DocumentFragment object
+ *        - Node object of type Template
+ *        - id selector: '#some-template-id'
+ *        - template string: '<div><span>{{msg}}</span></div>'
+ * @param {Boolean} shouldClone
+ * @param {Boolean} raw
+ *        inline HTML interpolation. Do not check for id
+ *        selector and keep whitespace in the string.
+ * @return {DocumentFragment|undefined}
+ */
+
+export function parseTemplate (template, shouldClone, raw) {
+  var node, frag
+
+  // if the template is already a document fragment,
+  // do nothing
+  if (isFragment(template)) {
+    trimNode(template)
+    return shouldClone
+      ? cloneNode(template)
+      : template
+  }
+
+  if (typeof template === 'string') {
+    // id selector
+    if (!raw && template.charAt(0) === '#') {
+      // id selector can be cached too
+      frag = idSelectorCache.get(template)
+      if (!frag) {
+        node = document.getElementById(template.slice(1))
+        if (node) {
+          frag = nodeToFragment(node)
+          // save selector to cache
+          idSelectorCache.put(template, frag)
+        }
+      }
+    } else {
+      // normal string template
+      frag = stringToFragment(template, raw)
+    }
+  } else if (template.nodeType) {
+    // a direct node
+    frag = nodeToFragment(template)
+  }
+
+  return frag && shouldClone
+    ? cloneNode(frag)
+    : frag
+}
diff --git a/src/parsers/text.js b/src/parsers/text.js
new file mode 100644
index 00000000000..4b2158c515b
--- /dev/null
+++ b/src/parsers/text.js
@@ -0,0 +1,164 @@
+import Cache from '../cache'
+import config from '../config'
+import { parseDirective } from '../parsers/directive'
+
+const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g
+let cache, tagRE, htmlRE
+
+/**
+ * Escape a string so it can be used in a RegExp
+ * constructor.
+ *
+ * @param {String} str
+ */
+
+function escapeRegex (str) {
+  return str.replace(regexEscapeRE, '\\$&')
+}
+
+export function compileRegex () {
+  var open = escapeRegex(config.delimiters[0])
+  var close = escapeRegex(config.delimiters[1])
+  var unsafeOpen = escapeRegex(config.unsafeDelimiters[0])
+  var unsafeClose = escapeRegex(config.unsafeDelimiters[1])
+  tagRE = new RegExp(
+    unsafeOpen + '((?:.|\\n)+?)' + unsafeClose + '|' +
+    open + '((?:.|\\n)+?)' + close,
+    'g'
+  )
+  htmlRE = new RegExp(
+    '^' + unsafeOpen + '((?:.|\\n)+?)' + unsafeClose + '$'
+  )
+  // reset cache
+  cache = new Cache(1000)
+}
+
+/**
+ * Parse a template text string into an array of tokens.
+ *
+ * @param {String} text
+ * @return {Array<Object> | null}
+ *               - {String} type
+ *               - {String} value
+ *               - {Boolean} [html]
+ *               - {Boolean} [oneTime]
+ */
+
+export function parseText (text) {
+  if (!cache) {
+    compileRegex()
+  }
+  var hit = cache.get(text)
+  if (hit) {
+    return hit
+  }
+  if (!tagRE.test(text)) {
+    return null
+  }
+  var tokens = []
+  var lastIndex = tagRE.lastIndex = 0
+  var match, index, html, value, first, oneTime
+  /* eslint-disable no-cond-assign */
+  while (match = tagRE.exec(text)) {
+  /* eslint-enable no-cond-assign */
+    index = match.index
+    // push text token
+    if (index > lastIndex) {
+      tokens.push({
+        value: text.slice(lastIndex, index)
+      })
+    }
+    // tag token
+    html = htmlRE.test(match[0])
+    value = html ? match[1] : match[2]
+    first = value.charCodeAt(0)
+    oneTime = first === 42 // *
+    value = oneTime
+      ? value.slice(1)
+      : value
+    tokens.push({
+      tag: true,
+      value: value.trim(),
+      html: html,
+      oneTime: oneTime
+    })
+    lastIndex = index + match[0].length
+  }
+  if (lastIndex < text.length) {
+    tokens.push({
+      value: text.slice(lastIndex)
+    })
+  }
+  cache.put(text, tokens)
+  return tokens
+}
+
+/**
+ * Format a list of tokens into an expression.
+ * e.g. tokens parsed from 'a {{b}} c' can be serialized
+ * into one single expression as '"a " + b + " c"'.
+ *
+ * @param {Array} tokens
+ * @param {Vue} [vm]
+ * @return {String}
+ */
+
+export function tokensToExp (tokens, vm) {
+  if (tokens.length > 1) {
+    return tokens.map(function (token) {
+      return formatToken(token, vm)
+    }).join('+')
+  } else {
+    return formatToken(tokens[0], vm, true)
+  }
+}
+
+/**
+ * Format a single token.
+ *
+ * @param {Object} token
+ * @param {Vue} [vm]
+ * @param {Boolean} [single]
+ * @return {String}
+ */
+
+function formatToken (token, vm, single) {
+  return token.tag
+    ? token.oneTime && vm
+      ? '"' + vm.$eval(token.value) + '"'
+      : inlineFilters(token.value, single)
+    : '"' + token.value + '"'
+}
+
+/**
+ * For an attribute with multiple interpolation tags,
+ * e.g. attr="some-{{thing | filter}}", in order to combine
+ * the whole thing into a single watchable expression, we
+ * have to inline those filters. This function does exactly
+ * that. This is a bit hacky but it avoids heavy changes
+ * to directive parser and watcher mechanism.
+ *
+ * @param {String} exp
+ * @param {Boolean} single
+ * @return {String}
+ */
+
+var filterRE = /[^|]\|[^|]/
+function inlineFilters (exp, single) {
+  if (!filterRE.test(exp)) {
+    return single
+      ? exp
+      : '(' + exp + ')'
+  } else {
+    var dir = parseDirective(exp)
+    if (!dir.filters) {
+      return '(' + exp + ')'
+    } else {
+      return 'this._applyFilters(' +
+        dir.expression + // value
+        ',null,' +       // oldValue (null for read)
+        JSON.stringify(dir.filters) + // filter descriptors
+        ',false)'        // write?
+    }
+  }
+}
diff --git a/src/platforms/web/compiler/directives/html.ts b/src/platforms/web/compiler/directives/html.ts
deleted file mode 100644
index 4aeac3fc4a1..00000000000
--- a/src/platforms/web/compiler/directives/html.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { addProp } from 'compiler/helpers'
-import { ASTDirective, ASTElement } from 'types/compiler'
-
-export default function html(el: ASTElement, dir: ASTDirective) {
-  if (dir.value) {
-    addProp(el, 'innerHTML', `_s(${dir.value})`, dir)
-  }
-}
diff --git a/src/platforms/web/compiler/directives/index.ts b/src/platforms/web/compiler/directives/index.ts
deleted file mode 100644
index 0955b51e7aa..00000000000
--- a/src/platforms/web/compiler/directives/index.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import model from './model'
-import text from './text'
-import html from './html'
-
-export default {
-  model,
-  text,
-  html
-}
diff --git a/src/platforms/web/compiler/directives/model.ts b/src/platforms/web/compiler/directives/model.ts
deleted file mode 100644
index ba6bb28ca2d..00000000000
--- a/src/platforms/web/compiler/directives/model.ts
+++ /dev/null
@@ -1,181 +0,0 @@
-import config from 'core/config'
-import { addHandler, addProp, getBindingAttr } from 'compiler/helpers'
-import { genComponentModel, genAssignmentCode } from 'compiler/directives/model'
-import { ASTDirective, ASTElement, ASTModifiers } from 'types/compiler'
-
-let warn
-
-// in some cases, the event used has to be determined at runtime
-// so we used some reserved tokens during compile.
-export const RANGE_TOKEN = '__r'
-export const CHECKBOX_RADIO_TOKEN = '__c'
-
-export default function model(
-  el: ASTElement,
-  dir: ASTDirective,
-  _warn: Function
-): boolean | undefined {
-  warn = _warn
-  const value = dir.value
-  const modifiers = dir.modifiers
-  const tag = el.tag
-  const type = el.attrsMap.type
-
-  if (__DEV__) {
-    // inputs with type="file" are read only and setting the input's
-    // value will throw an error.
-    if (tag === 'input' && type === 'file') {
-      warn(
-        `<${el.tag} v-model="${value}" type="file">:\n` +
-          `File inputs are read only. Use a v-on:change listener instead.`,
-        el.rawAttrsMap['v-model']
-      )
-    }
-  }
-
-  if (el.component) {
-    genComponentModel(el, value, modifiers)
-    // component v-model doesn't need extra runtime
-    return false
-  } else if (tag === 'select') {
-    genSelect(el, value, modifiers)
-  } else if (tag === 'input' && type === 'checkbox') {
-    genCheckboxModel(el, value, modifiers)
-  } else if (tag === 'input' && type === 'radio') {
-    genRadioModel(el, value, modifiers)
-  } else if (tag === 'input' || tag === 'textarea') {
-    genDefaultModel(el, value, modifiers)
-  } else if (!config.isReservedTag(tag)) {
-    genComponentModel(el, value, modifiers)
-    // component v-model doesn't need extra runtime
-    return false
-  } else if (__DEV__) {
-    warn(
-      `<${el.tag} v-model="${value}">: ` +
-        `v-model is not supported on this element type. ` +
-        "If you are working with contenteditable, it's recommended to " +
-        'wrap a library dedicated for that purpose inside a custom component.',
-      el.rawAttrsMap['v-model']
-    )
-  }
-
-  // ensure runtime directive metadata
-  return true
-}
-
-function genCheckboxModel(
-  el: ASTElement,
-  value: string,
-  modifiers?: ASTModifiers | null
-) {
-  const number = modifiers && modifiers.number
-  const valueBinding = getBindingAttr(el, 'value') || 'null'
-  const trueValueBinding = getBindingAttr(el, 'true-value') || 'true'
-  const falseValueBinding = getBindingAttr(el, 'false-value') || 'false'
-  addProp(
-    el,
-    'checked',
-    `Array.isArray(${value})` +
-      `?_i(${value},${valueBinding})>-1` +
-      (trueValueBinding === 'true'
-        ? `:(${value})`
-        : `:_q(${value},${trueValueBinding})`)
-  )
-  addHandler(
-    el,
-    'change',
-    `var $$a=${value},` +
-      '$$el=$event.target,' +
-      `$$c=$$el.checked?(${trueValueBinding}):(${falseValueBinding});` +
-      'if(Array.isArray($$a)){' +
-      `var $$v=${number ? '_n(' + valueBinding + ')' : valueBinding},` +
-      '$$i=_i($$a,$$v);' +
-      `if($$el.checked){$$i<0&&(${genAssignmentCode(
-        value,
-        '$$a.concat([$$v])'
-      )})}` +
-      `else{$$i>-1&&(${genAssignmentCode(
-        value,
-        '$$a.slice(0,$$i).concat($$a.slice($$i+1))'
-      )})}` +
-      `}else{${genAssignmentCode(value, '$$c')}}`,
-    null,
-    true
-  )
-}
-
-function genRadioModel(
-  el: ASTElement,
-  value: string,
-  modifiers?: ASTModifiers | null
-) {
-  const number = modifiers && modifiers.number
-  let valueBinding = getBindingAttr(el, 'value') || 'null'
-  valueBinding = number ? `_n(${valueBinding})` : valueBinding
-  addProp(el, 'checked', `_q(${value},${valueBinding})`)
-  addHandler(el, 'change', genAssignmentCode(value, valueBinding), null, true)
-}
-
-function genSelect(
-  el: ASTElement,
-  value: string,
-  modifiers?: ASTModifiers | null
-) {
-  const number = modifiers && modifiers.number
-  const selectedVal =
-    `Array.prototype.filter` +
-    `.call($event.target.options,function(o){return o.selected})` +
-    `.map(function(o){var val = "_value" in o ? o._value : o.value;` +
-    `return ${number ? '_n(val)' : 'val'}})`
-
-  const assignment = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'
-  let code = `var $$selectedVal = ${selectedVal};`
-  code = `${code} ${genAssignmentCode(value, assignment)}`
-  addHandler(el, 'change', code, null, true)
-}
-
-function genDefaultModel(
-  el: ASTElement,
-  value: string,
-  modifiers?: ASTModifiers | null
-): boolean | void {
-  const type = el.attrsMap.type
-
-  // warn if v-bind:value conflicts with v-model
-  // except for inputs with v-bind:type
-  if (__DEV__) {
-    const value = el.attrsMap['v-bind:value'] || el.attrsMap[':value']
-    const typeBinding = el.attrsMap['v-bind:type'] || el.attrsMap[':type']
-    if (value && !typeBinding) {
-      const binding = el.attrsMap['v-bind:value'] ? 'v-bind:value' : ':value'
-      warn(
-        `${binding}="${value}" conflicts with v-model on the same element ` +
-          'because the latter already expands to a value binding internally',
-        el.rawAttrsMap[binding]
-      )
-    }
-  }
-
-  const { lazy, number, trim } = modifiers || {}
-  const needCompositionGuard = !lazy && type !== 'range'
-  const event = lazy ? 'change' : type === 'range' ? RANGE_TOKEN : 'input'
-
-  let valueExpression = '$event.target.value'
-  if (trim) {
-    valueExpression = `$event.target.value.trim()`
-  }
-  if (number) {
-    valueExpression = `_n(${valueExpression})`
-  }
-
-  let code = genAssignmentCode(value, valueExpression)
-  if (needCompositionGuard) {
-    code = `if($event.target.composing)return;${code}`
-  }
-
-  addProp(el, 'value', `(${value})`)
-  addHandler(el, event, code, null, true)
-  if (trim || number) {
-    addHandler(el, 'blur', '$forceUpdate()')
-  }
-}
diff --git a/src/platforms/web/compiler/directives/text.ts b/src/platforms/web/compiler/directives/text.ts
deleted file mode 100644
index cabfd583124..00000000000
--- a/src/platforms/web/compiler/directives/text.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { addProp } from 'compiler/helpers'
-import { ASTDirective, ASTElement } from 'types/compiler'
-
-export default function text(el: ASTElement, dir: ASTDirective) {
-  if (dir.value) {
-    addProp(el, 'textContent', `_s(${dir.value})`, dir)
-  }
-}
diff --git a/src/platforms/web/compiler/index.ts b/src/platforms/web/compiler/index.ts
deleted file mode 100644
index 857351f15f5..00000000000
--- a/src/platforms/web/compiler/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { baseOptions } from './options'
-import { createCompiler } from 'compiler/index'
-
-const { compile, compileToFunctions } = createCompiler(baseOptions)
-
-export { compile, compileToFunctions }
diff --git a/src/platforms/web/compiler/modules/class.ts b/src/platforms/web/compiler/modules/class.ts
deleted file mode 100644
index b7837871459..00000000000
--- a/src/platforms/web/compiler/modules/class.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { parseText } from 'compiler/parser/text-parser'
-import { getAndRemoveAttr, getBindingAttr, baseWarn } from 'compiler/helpers'
-import { ASTElement, CompilerOptions, ModuleOptions } from 'types/compiler'
-
-function transformNode(el: ASTElement, options: CompilerOptions) {
-  const warn = options.warn || baseWarn
-  const staticClass = getAndRemoveAttr(el, 'class')
-  if (__DEV__ && staticClass) {
-    const res = parseText(staticClass, options.delimiters)
-    if (res) {
-      warn(
-        `class="${staticClass}": ` +
-          'Interpolation inside attributes has been removed. ' +
-          'Use v-bind or the colon shorthand instead. For example, ' +
-          'instead of <div class="{{ val }}">, use <div :class="val">.',
-        el.rawAttrsMap['class']
-      )
-    }
-  }
-  if (staticClass) {
-    el.staticClass = JSON.stringify(staticClass.replace(/\s+/g, ' ').trim())
-  }
-  const classBinding = getBindingAttr(el, 'class', false /* getStatic */)
-  if (classBinding) {
-    el.classBinding = classBinding
-  }
-}
-
-function genData(el: ASTElement): string {
-  let data = ''
-  if (el.staticClass) {
-    data += `staticClass:${el.staticClass},`
-  }
-  if (el.classBinding) {
-    data += `class:${el.classBinding},`
-  }
-  return data
-}
-
-export default {
-  staticKeys: ['staticClass'],
-  transformNode,
-  genData
-} as ModuleOptions
diff --git a/src/platforms/web/compiler/modules/index.ts b/src/platforms/web/compiler/modules/index.ts
deleted file mode 100644
index 438f93d2953..00000000000
--- a/src/platforms/web/compiler/modules/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import klass from './class'
-import style from './style'
-import model from './model'
-
-export default [klass, style, model]
diff --git a/src/platforms/web/compiler/modules/model.ts b/src/platforms/web/compiler/modules/model.ts
deleted file mode 100644
index 131483a83a1..00000000000
--- a/src/platforms/web/compiler/modules/model.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * Expand input[v-model] with dynamic type bindings into v-if-else chains
- * Turn this:
- *   <input v-model="data[type]" :type="type">
- * into this:
- *   <input v-if="type === 'checkbox'" type="checkbox" v-model="data[type]">
- *   <input v-else-if="type === 'radio'" type="radio" v-model="data[type]">
- *   <input v-else :type="type" v-model="data[type]">
- */
-
-import { addRawAttr, getBindingAttr, getAndRemoveAttr } from 'compiler/helpers'
-
-import {
-  processFor,
-  processElement,
-  addIfCondition,
-  createASTElement
-} from 'compiler/parser/index'
-import { ASTElement, CompilerOptions, ModuleOptions } from 'types/compiler'
-
-function preTransformNode(el: ASTElement, options: CompilerOptions) {
-  if (el.tag === 'input') {
-    const map = el.attrsMap
-    if (!map['v-model']) {
-      return
-    }
-
-    let typeBinding
-    if (map[':type'] || map['v-bind:type']) {
-      typeBinding = getBindingAttr(el, 'type')
-    }
-    if (!map.type && !typeBinding && map['v-bind']) {
-      typeBinding = `(${map['v-bind']}).type`
-    }
-
-    if (typeBinding) {
-      const ifCondition = getAndRemoveAttr(el, 'v-if', true)
-      const ifConditionExtra = ifCondition ? `&&(${ifCondition})` : ``
-      const hasElse = getAndRemoveAttr(el, 'v-else', true) != null
-      const elseIfCondition = getAndRemoveAttr(el, 'v-else-if', true)
-      // 1. checkbox
-      const branch0 = cloneASTElement(el)
-      // process for on the main node
-      processFor(branch0)
-      addRawAttr(branch0, 'type', 'checkbox')
-      processElement(branch0, options)
-      branch0.processed = true // prevent it from double-processed
-      branch0.if = `(${typeBinding})==='checkbox'` + ifConditionExtra
-      addIfCondition(branch0, {
-        exp: branch0.if,
-        block: branch0
-      })
-      // 2. add radio else-if condition
-      const branch1 = cloneASTElement(el)
-      getAndRemoveAttr(branch1, 'v-for', true)
-      addRawAttr(branch1, 'type', 'radio')
-      processElement(branch1, options)
-      addIfCondition(branch0, {
-        exp: `(${typeBinding})==='radio'` + ifConditionExtra,
-        block: branch1
-      })
-      // 3. other
-      const branch2 = cloneASTElement(el)
-      getAndRemoveAttr(branch2, 'v-for', true)
-      addRawAttr(branch2, ':type', typeBinding)
-      processElement(branch2, options)
-      addIfCondition(branch0, {
-        exp: ifCondition!,
-        block: branch2
-      })
-
-      if (hasElse) {
-        branch0.else = true
-      } else if (elseIfCondition) {
-        branch0.elseif = elseIfCondition
-      }
-
-      return branch0
-    }
-  }
-}
-
-function cloneASTElement(el) {
-  return createASTElement(el.tag, el.attrsList.slice(), el.parent)
-}
-
-export default {
-  preTransformNode
-} as ModuleOptions
diff --git a/src/platforms/web/compiler/modules/style.ts b/src/platforms/web/compiler/modules/style.ts
deleted file mode 100644
index 67e8524a4bb..00000000000
--- a/src/platforms/web/compiler/modules/style.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { parseText } from 'compiler/parser/text-parser'
-import { parseStyleText } from 'web/util/style'
-import { getAndRemoveAttr, getBindingAttr, baseWarn } from 'compiler/helpers'
-import { ASTElement, CompilerOptions, ModuleOptions } from 'types/compiler'
-
-function transformNode(el: ASTElement, options: CompilerOptions) {
-  const warn = options.warn || baseWarn
-  const staticStyle = getAndRemoveAttr(el, 'style')
-  if (staticStyle) {
-    /* istanbul ignore if */
-    if (__DEV__) {
-      const res = parseText(staticStyle, options.delimiters)
-      if (res) {
-        warn(
-          `style="${staticStyle}": ` +
-            'Interpolation inside attributes has been removed. ' +
-            'Use v-bind or the colon shorthand instead. For example, ' +
-            'instead of <div style="{{ val }}">, use <div :style="val">.',
-          el.rawAttrsMap['style']
-        )
-      }
-    }
-    el.staticStyle = JSON.stringify(parseStyleText(staticStyle))
-  }
-
-  const styleBinding = getBindingAttr(el, 'style', false /* getStatic */)
-  if (styleBinding) {
-    el.styleBinding = styleBinding
-  }
-}
-
-function genData(el: ASTElement): string {
-  let data = ''
-  if (el.staticStyle) {
-    data += `staticStyle:${el.staticStyle},`
-  }
-  if (el.styleBinding) {
-    data += `style:(${el.styleBinding}),`
-  }
-  return data
-}
-
-export default {
-  staticKeys: ['staticStyle'],
-  transformNode,
-  genData
-} as ModuleOptions
diff --git a/src/platforms/web/compiler/options.ts b/src/platforms/web/compiler/options.ts
deleted file mode 100644
index 29e570ce3b8..00000000000
--- a/src/platforms/web/compiler/options.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import {
-  isPreTag,
-  mustUseProp,
-  isReservedTag,
-  getTagNamespace
-} from '../util/index'
-
-import modules from './modules/index'
-import directives from './directives/index'
-import { genStaticKeys } from 'shared/util'
-import { isUnaryTag, canBeLeftOpenTag } from './util'
-import { CompilerOptions } from 'types/compiler'
-
-export const baseOptions: CompilerOptions = {
-  expectHTML: true,
-  modules,
-  directives,
-  isPreTag,
-  isUnaryTag,
-  mustUseProp,
-  canBeLeftOpenTag,
-  isReservedTag,
-  getTagNamespace,
-  staticKeys: genStaticKeys(modules)
-}
diff --git a/src/platforms/web/compiler/util.ts b/src/platforms/web/compiler/util.ts
deleted file mode 100644
index 097e0a47c97..00000000000
--- a/src/platforms/web/compiler/util.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { makeMap } from 'shared/util'
-
-export const isUnaryTag = makeMap(
-  'area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' +
-    'link,meta,param,source,track,wbr'
-)
-
-// Elements that you can, intentionally, leave open
-// (and which close themselves)
-export const canBeLeftOpenTag = makeMap(
-  'colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source'
-)
-
-// HTML5 tags https://html.spec.whatwg.org/multipage/indices.html#elements-3
-// Phrasing Content https://html.spec.whatwg.org/multipage/dom.html#phrasing-content
-export const isNonPhrasingTag = makeMap(
-  'address,article,aside,base,blockquote,body,caption,col,colgroup,dd,' +
-    'details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,' +
-    'h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,' +
-    'optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,' +
-    'title,tr,track'
-)
diff --git a/src/platforms/web/entry-compiler.ts b/src/platforms/web/entry-compiler.ts
deleted file mode 100644
index 659766493d0..00000000000
--- a/src/platforms/web/entry-compiler.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export { parseComponent } from 'sfc/parseComponent'
-export { compile, compileToFunctions } from './compiler/index'
-export { ssrCompile, ssrCompileToFunctions } from 'server/compiler'
-export { generateCodeFrame } from 'compiler/codeframe'
diff --git a/src/platforms/web/entry-runtime-esm.ts b/src/platforms/web/entry-runtime-esm.ts
deleted file mode 100644
index f858d03d0df..00000000000
--- a/src/platforms/web/entry-runtime-esm.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import Vue from './runtime/index'
-
-export default Vue
-
-export * from 'v3'
diff --git a/src/platforms/web/entry-runtime-with-compiler-esm.ts b/src/platforms/web/entry-runtime-with-compiler-esm.ts
deleted file mode 100644
index ff2ac5c928e..00000000000
--- a/src/platforms/web/entry-runtime-with-compiler-esm.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import Vue from './runtime-with-compiler'
-
-export default Vue
-
-export * from 'v3'
diff --git a/src/platforms/web/entry-runtime-with-compiler.ts b/src/platforms/web/entry-runtime-with-compiler.ts
deleted file mode 100644
index a28aea80dc4..00000000000
--- a/src/platforms/web/entry-runtime-with-compiler.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import Vue from './runtime-with-compiler'
-import * as vca from 'v3'
-import { extend } from 'shared/util'
-
-extend(Vue, vca)
-
-import { effect } from 'v3/reactivity/effect'
-Vue.effect = effect
-
-export default Vue
diff --git a/src/platforms/web/entry-runtime.ts b/src/platforms/web/entry-runtime.ts
deleted file mode 100644
index 86cea399ec5..00000000000
--- a/src/platforms/web/entry-runtime.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import Vue from './runtime/index'
-import * as vca from 'v3'
-import { extend } from 'shared/util'
-
-extend(Vue, vca)
-
-export default Vue
diff --git a/src/platforms/web/runtime-with-compiler.ts b/src/platforms/web/runtime-with-compiler.ts
deleted file mode 100644
index 0dc1d132d38..00000000000
--- a/src/platforms/web/runtime-with-compiler.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import config from 'core/config'
-import { warn, cached } from 'core/util/index'
-import { mark, measure } from 'core/util/perf'
-
-import Vue from './runtime/index'
-import { query } from './util/index'
-import { compileToFunctions } from './compiler/index'
-import {
-  shouldDecodeNewlines,
-  shouldDecodeNewlinesForHref
-} from './util/compat'
-import type { Component } from 'types/component'
-import type { GlobalAPI } from 'types/global-api'
-
-const idToTemplate = cached(id => {
-  const el = query(id)
-  return el && el.innerHTML
-})
-
-const mount = Vue.prototype.$mount
-Vue.prototype.$mount = function (
-  el?: string | Element,
-  hydrating?: boolean
-): Component {
-  el = el && query(el)
-
-  /* istanbul ignore if */
-  if (el === document.body || el === document.documentElement) {
-    __DEV__ &&
-      warn(
-        `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
-      )
-    return this
-  }
-
-  const options = this.$options
-  // resolve template/el and convert to render function
-  if (!options.render) {
-    let template = options.template
-    if (template) {
-      if (typeof template === 'string') {
-        if (template.charAt(0) === '#') {
-          template = idToTemplate(template)
-          /* istanbul ignore if */
-          if (__DEV__ && !template) {
-            warn(
-              `Template element not found or is empty: ${options.template}`,
-              this
-            )
-          }
-        }
-      } else if (template.nodeType) {
-        template = template.innerHTML
-      } else {
-        if (__DEV__) {
-          warn('invalid template option:' + template, this)
-        }
-        return this
-      }
-    } else if (el) {
-      // @ts-expect-error
-      template = getOuterHTML(el)
-    }
-    if (template) {
-      /* istanbul ignore if */
-      if (__DEV__ && config.performance && mark) {
-        mark('compile')
-      }
-
-      const { render, staticRenderFns } = compileToFunctions(
-        template,
-        {
-          outputSourceRange: __DEV__,
-          shouldDecodeNewlines,
-          shouldDecodeNewlinesForHref,
-          delimiters: options.delimiters,
-          comments: options.comments
-        },
-        this
-      )
-      options.render = render
-      options.staticRenderFns = staticRenderFns
-
-      /* istanbul ignore if */
-      if (__DEV__ && config.performance && mark) {
-        mark('compile end')
-        measure(`vue ${this._name} compile`, 'compile', 'compile end')
-      }
-    }
-  }
-  return mount.call(this, el, hydrating)
-}
-
-/**
- * Get outerHTML of elements, taking care
- * of SVG elements in IE as well.
- */
-function getOuterHTML(el: Element): string {
-  if (el.outerHTML) {
-    return el.outerHTML
-  } else {
-    const container = document.createElement('div')
-    container.appendChild(el.cloneNode(true))
-    return container.innerHTML
-  }
-}
-
-Vue.compile = compileToFunctions
-
-export default Vue as GlobalAPI
diff --git a/src/platforms/web/runtime/class-util.ts b/src/platforms/web/runtime/class-util.ts
deleted file mode 100644
index e66ffae5878..00000000000
--- a/src/platforms/web/runtime/class-util.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-const whitespaceRE = /\s+/
-
-/**
- * Add class with compatibility for SVG since classList is not supported on
- * SVG elements in IE
- */
-export function addClass(el: HTMLElement, cls?: string) {
-  /* istanbul ignore if */
-  if (!cls || !(cls = cls.trim())) {
-    return
-  }
-
-  /* istanbul ignore else */
-  if (el.classList) {
-    if (cls.indexOf(' ') > -1) {
-      cls.split(whitespaceRE).forEach(c => el.classList.add(c))
-    } else {
-      el.classList.add(cls)
-    }
-  } else {
-    const cur = ` ${el.getAttribute('class') || ''} `
-    if (cur.indexOf(' ' + cls + ' ') < 0) {
-      el.setAttribute('class', (cur + cls).trim())
-    }
-  }
-}
-
-/**
- * Remove class with compatibility for SVG since classList is not supported on
- * SVG elements in IE
- */
-export function removeClass(el: HTMLElement, cls?: string) {
-  /* istanbul ignore if */
-  if (!cls || !(cls = cls.trim())) {
-    return
-  }
-
-  /* istanbul ignore else */
-  if (el.classList) {
-    if (cls.indexOf(' ') > -1) {
-      cls.split(whitespaceRE).forEach(c => el.classList.remove(c))
-    } else {
-      el.classList.remove(cls)
-    }
-    if (!el.classList.length) {
-      el.removeAttribute('class')
-    }
-  } else {
-    let cur = ` ${el.getAttribute('class') || ''} `
-    const tar = ' ' + cls + ' '
-    while (cur.indexOf(tar) >= 0) {
-      cur = cur.replace(tar, ' ')
-    }
-    cur = cur.trim()
-    if (cur) {
-      el.setAttribute('class', cur)
-    } else {
-      el.removeAttribute('class')
-    }
-  }
-}
diff --git a/src/platforms/web/runtime/components/index.ts b/src/platforms/web/runtime/components/index.ts
deleted file mode 100644
index 6bfe5780a5a..00000000000
--- a/src/platforms/web/runtime/components/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import Transition from './transition'
-import TransitionGroup from './transition-group'
-
-export default {
-  Transition,
-  TransitionGroup
-}
diff --git a/src/platforms/web/runtime/components/transition-group.ts b/src/platforms/web/runtime/components/transition-group.ts
deleted file mode 100644
index 8588ea80561..00000000000
--- a/src/platforms/web/runtime/components/transition-group.ts
+++ /dev/null
@@ -1,204 +0,0 @@
-// Provides transition support for list items.
-// supports move transitions using the FLIP technique.
-
-// Because the vdom's children update algorithm is "unstable" - i.e.
-// it doesn't guarantee the relative positioning of removed elements,
-// we force transition-group to update its children into two passes:
-// in the first pass, we remove all nodes that need to be removed,
-// triggering their leaving transition; in the second pass, we insert/move
-// into the final desired state. This way in the second pass removed
-// nodes will remain where they should be.
-
-import { warn, extend } from 'core/util/index'
-import { addClass, removeClass } from 'web/runtime/class-util'
-import { transitionProps, extractTransitionData } from './transition'
-import { setActiveInstance } from 'core/instance/lifecycle'
-
-import {
-  hasTransition,
-  getTransitionInfo,
-  transitionEndEvent,
-  addTransitionClass,
-  removeTransitionClass
-} from 'web/runtime/transition-util'
-import VNode from 'core/vdom/vnode'
-import { VNodeWithData } from 'types/vnode'
-import { getComponentName } from 'core/vdom/create-component'
-
-const props = extend(
-  {
-    tag: String,
-    moveClass: String
-  },
-  transitionProps
-)
-
-delete props.mode
-
-export default {
-  props,
-
-  beforeMount() {
-    const update = this._update
-    this._update = (vnode, hydrating) => {
-      const restoreActiveInstance = setActiveInstance(this)
-      // force removing pass
-      this.__patch__(
-        this._vnode,
-        this.kept,
-        false, // hydrating
-        true // removeOnly (!important, avoids unnecessary moves)
-      )
-      this._vnode = this.kept
-      restoreActiveInstance()
-      update.call(this, vnode, hydrating)
-    }
-  },
-
-  render(h: Function) {
-    const tag: string = this.tag || this.$vnode.data.tag || 'span'
-    const map: Record<string, any> = Object.create(null)
-    const prevChildren: Array<VNode> = (this.prevChildren = this.children)
-    const rawChildren: Array<VNode> = this.$slots.default || []
-    const children: Array<VNode> = (this.children = [])
-    const transitionData = extractTransitionData(this)
-
-    for (let i = 0; i < rawChildren.length; i++) {
-      const c: VNode = rawChildren[i]
-      if (c.tag) {
-        if (c.key != null && String(c.key).indexOf('__vlist') !== 0) {
-          children.push(c)
-          map[c.key] = c
-          ;(c.data || (c.data = {})).transition = transitionData
-        } else if (__DEV__) {
-          const opts = c.componentOptions
-          const name: string = opts
-            ? getComponentName(opts.Ctor.options as any) || opts.tag || ''
-            : c.tag
-          warn(`<transition-group> children must be keyed: <${name}>`)
-        }
-      }
-    }
-
-    if (prevChildren) {
-      const kept: Array<VNode> = []
-      const removed: Array<VNode> = []
-      for (let i = 0; i < prevChildren.length; i++) {
-        const c: VNode = prevChildren[i]
-        c.data!.transition = transitionData
-        // @ts-expect-error .getBoundingClientRect is not typed in Node
-        c.data!.pos = c.elm.getBoundingClientRect()
-        if (map[c.key!]) {
-          kept.push(c)
-        } else {
-          removed.push(c)
-        }
-      }
-      this.kept = h(tag, null, kept)
-      this.removed = removed
-    }
-
-    return h(tag, null, children)
-  },
-
-  updated() {
-    const children: Array<VNodeWithData> = this.prevChildren
-    const moveClass: string = this.moveClass || (this.name || 'v') + '-move'
-    if (!children.length || !this.hasMove(children[0].elm, moveClass)) {
-      return
-    }
-
-    // we divide the work into three loops to avoid mixing DOM reads and writes
-    // in each iteration - which helps prevent layout thrashing.
-    children.forEach(callPendingCbs)
-    children.forEach(recordPosition)
-    children.forEach(applyTranslation)
-
-    // force reflow to put everything in position
-    // assign to this to avoid being removed in tree-shaking
-    // $flow-disable-line
-    this._reflow = document.body.offsetHeight
-
-    children.forEach((c: VNode) => {
-      if (c.data!.moved) {
-        const el: any = c.elm
-        const s: any = el.style
-        addTransitionClass(el, moveClass)
-        s.transform = s.WebkitTransform = s.transitionDuration = ''
-        el.addEventListener(
-          transitionEndEvent,
-          (el._moveCb = function cb(e) {
-            if (e && e.target !== el) {
-              return
-            }
-            if (!e || /transform$/.test(e.propertyName)) {
-              el.removeEventListener(transitionEndEvent, cb)
-              el._moveCb = null
-              removeTransitionClass(el, moveClass)
-            }
-          })
-        )
-      }
-    })
-  },
-
-  methods: {
-    hasMove(el: any, moveClass: string): boolean {
-      /* istanbul ignore if */
-      if (!hasTransition) {
-        return false
-      }
-      /* istanbul ignore if */
-      if (this._hasMove) {
-        return this._hasMove
-      }
-      // Detect whether an element with the move class applied has
-      // CSS transitions. Since the element may be inside an entering
-      // transition at this very moment, we make a clone of it and remove
-      // all other transition classes applied to ensure only the move class
-      // is applied.
-      const clone: HTMLElement = el.cloneNode()
-      if (el._transitionClasses) {
-        el._transitionClasses.forEach((cls: string) => {
-          removeClass(clone, cls)
-        })
-      }
-      addClass(clone, moveClass)
-      clone.style.display = 'none'
-      this.$el.appendChild(clone)
-      const info: any = getTransitionInfo(clone)
-      this.$el.removeChild(clone)
-      return (this._hasMove = info.hasTransform)
-    }
-  }
-}
-
-function callPendingCbs(
-  c: VNodeWithData & { elm?: { _moveCb?: Function; _enterCb?: Function } }
-) {
-  /* istanbul ignore if */
-  if (c.elm!._moveCb) {
-    c.elm!._moveCb()
-  }
-  /* istanbul ignore if */
-  if (c.elm!._enterCb) {
-    c.elm!._enterCb()
-  }
-}
-
-function recordPosition(c: VNodeWithData) {
-  c.data!.newPos = c.elm.getBoundingClientRect()
-}
-
-function applyTranslation(c: VNodeWithData) {
-  const oldPos = c.data.pos
-  const newPos = c.data.newPos
-  const dx = oldPos.left - newPos.left
-  const dy = oldPos.top - newPos.top
-  if (dx || dy) {
-    c.data.moved = true
-    const s = c.elm.style
-    s.transform = s.WebkitTransform = `translate(${dx}px,${dy}px)`
-    s.transitionDuration = '0s'
-  }
-}
diff --git a/src/platforms/web/runtime/components/transition.ts b/src/platforms/web/runtime/components/transition.ts
deleted file mode 100644
index 8f5c05679ef..00000000000
--- a/src/platforms/web/runtime/components/transition.ts
+++ /dev/null
@@ -1,205 +0,0 @@
-// Provides transition support for a single element/component.
-// supports transition mode (out-in / in-out)
-
-import { warn } from 'core/util/index'
-import { camelize, extend, isPrimitive } from 'shared/util'
-import {
-  mergeVNodeHook,
-  isAsyncPlaceholder,
-  getFirstComponentChild
-} from 'core/vdom/helpers/index'
-import VNode from 'core/vdom/vnode'
-import type { Component } from 'types/component'
-
-export const transitionProps = {
-  name: String,
-  appear: Boolean,
-  css: Boolean,
-  mode: String,
-  type: String,
-  enterClass: String,
-  leaveClass: String,
-  enterToClass: String,
-  leaveToClass: String,
-  enterActiveClass: String,
-  leaveActiveClass: String,
-  appearClass: String,
-  appearActiveClass: String,
-  appearToClass: String,
-  duration: [Number, String, Object]
-}
-
-// in case the child is also an abstract component, e.g. <keep-alive>
-// we want to recursively retrieve the real component to be rendered
-function getRealChild(vnode?: VNode): VNode | undefined {
-  const compOptions = vnode && vnode.componentOptions
-  if (compOptions && compOptions.Ctor.options.abstract) {
-    return getRealChild(getFirstComponentChild(compOptions.children))
-  } else {
-    return vnode
-  }
-}
-
-export function extractTransitionData(comp: Component): Record<string, any> {
-  const data = {}
-  const options = comp.$options
-  // props
-  for (const key in options.propsData) {
-    data[key] = comp[key]
-  }
-  // events.
-  // extract listeners and pass them directly to the transition methods
-  const listeners = options._parentListeners
-  for (const key in listeners) {
-    data[camelize(key)] = listeners[key]
-  }
-  return data
-}
-
-function placeholder(h: Function, rawChild: VNode): VNode | undefined {
-  // @ts-expect-error
-  if (/\d-keep-alive$/.test(rawChild.tag)) {
-    return h('keep-alive', {
-      props: rawChild.componentOptions!.propsData
-    })
-  }
-}
-
-function hasParentTransition(vnode: VNode): boolean | undefined {
-  while ((vnode = vnode.parent!)) {
-    if (vnode.data!.transition) {
-      return true
-    }
-  }
-}
-
-function isSameChild(child: VNode, oldChild: VNode): boolean {
-  return oldChild.key === child.key && oldChild.tag === child.tag
-}
-
-const isNotTextNode = (c: VNode) => c.tag || isAsyncPlaceholder(c)
-
-const isVShowDirective = d => d.name === 'show'
-
-export default {
-  name: 'transition',
-  props: transitionProps,
-  abstract: true,
-
-  render(h: Function) {
-    let children: any = this.$slots.default
-    if (!children) {
-      return
-    }
-
-    // filter out text nodes (possible whitespaces)
-    children = children.filter(isNotTextNode)
-    /* istanbul ignore if */
-    if (!children.length) {
-      return
-    }
-
-    // warn multiple elements
-    if (__DEV__ && children.length > 1) {
-      warn(
-        '<transition> can only be used on a single element. Use ' +
-          '<transition-group> for lists.',
-        this.$parent
-      )
-    }
-
-    const mode: string = this.mode
-
-    // warn invalid mode
-    if (__DEV__ && mode && mode !== 'in-out' && mode !== 'out-in') {
-      warn('invalid <transition> mode: ' + mode, this.$parent)
-    }
-
-    const rawChild: VNode = children[0]
-
-    // if this is a component root node and the component's
-    // parent container node also has transition, skip.
-    if (hasParentTransition(this.$vnode)) {
-      return rawChild
-    }
-
-    // apply transition data to child
-    // use getRealChild() to ignore abstract components e.g. keep-alive
-    const child = getRealChild(rawChild)
-    /* istanbul ignore if */
-    if (!child) {
-      return rawChild
-    }
-
-    if (this._leaving) {
-      return placeholder(h, rawChild)
-    }
-
-    // ensure a key that is unique to the vnode type and to this transition
-    // component instance. This key will be used to remove pending leaving nodes
-    // during entering.
-    const id: string = `__transition-${this._uid}-`
-    child.key =
-      child.key == null
-        ? child.isComment
-          ? id + 'comment'
-          : id + child.tag
-        : isPrimitive(child.key)
-        ? String(child.key).indexOf(id) === 0
-          ? child.key
-          : id + child.key
-        : child.key
-
-    const data: Object = ((child.data || (child.data = {})).transition =
-      extractTransitionData(this))
-    const oldRawChild: VNode = this._vnode
-    const oldChild = getRealChild(oldRawChild)
-
-    // mark v-show
-    // so that the transition module can hand over the control to the directive
-    if (child.data.directives && child.data.directives.some(isVShowDirective)) {
-      child.data.show = true
-    }
-
-    if (
-      oldChild &&
-      oldChild.data &&
-      !isSameChild(child, oldChild) &&
-      !isAsyncPlaceholder(oldChild) &&
-      // #6687 component root is a comment node
-      !(
-        oldChild.componentInstance &&
-        oldChild.componentInstance._vnode!.isComment
-      )
-    ) {
-      // replace old child transition data with fresh one
-      // important for dynamic transitions!
-      const oldData: Object = (oldChild.data.transition = extend({}, data))
-      // handle transition mode
-      if (mode === 'out-in') {
-        // return placeholder node and queue update when leave finishes
-        this._leaving = true
-        mergeVNodeHook(oldData, 'afterLeave', () => {
-          this._leaving = false
-          this.$forceUpdate()
-        })
-        return placeholder(h, rawChild)
-      } else if (mode === 'in-out') {
-        if (isAsyncPlaceholder(child)) {
-          return oldRawChild
-        }
-        let delayedLeave
-        const performLeave = () => {
-          delayedLeave()
-        }
-        mergeVNodeHook(data, 'afterEnter', performLeave)
-        mergeVNodeHook(data, 'enterCancelled', performLeave)
-        mergeVNodeHook(oldData, 'delayLeave', leave => {
-          delayedLeave = leave
-        })
-      }
-    }
-
-    return rawChild
-  }
-}
diff --git a/src/platforms/web/runtime/directives/index.ts b/src/platforms/web/runtime/directives/index.ts
deleted file mode 100644
index b673f11aa61..00000000000
--- a/src/platforms/web/runtime/directives/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import model from './model'
-import show from './show'
-
-export default {
-  model,
-  show
-}
diff --git a/src/platforms/web/runtime/directives/model.ts b/src/platforms/web/runtime/directives/model.ts
deleted file mode 100644
index b5430894f6e..00000000000
--- a/src/platforms/web/runtime/directives/model.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-/**
- * Not type checking this file because flow doesn't like attaching
- * properties to Elements.
- */
-
-import { isTextInputType } from 'web/util/element'
-import { looseEqual, looseIndexOf } from 'shared/util'
-import { mergeVNodeHook } from 'core/vdom/helpers/index'
-import { warn, isIE9, isIE, isEdge } from 'core/util/index'
-
-/* istanbul ignore if */
-if (isIE9) {
-  // http://www.matts411.com/post/internet-explorer-9-oninput/
-  document.addEventListener('selectionchange', () => {
-    const el = document.activeElement
-    // @ts-expect-error
-    if (el && el.vmodel) {
-      trigger(el, 'input')
-    }
-  })
-}
-
-const directive = {
-  inserted(el, binding, vnode, oldVnode) {
-    if (vnode.tag === 'select') {
-      // #6903
-      if (oldVnode.elm && !oldVnode.elm._vOptions) {
-        mergeVNodeHook(vnode, 'postpatch', () => {
-          directive.componentUpdated(el, binding, vnode)
-        })
-      } else {
-        setSelected(el, binding, vnode.context)
-      }
-      el._vOptions = [].map.call(el.options, getValue)
-    } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
-      el._vModifiers = binding.modifiers
-      if (!binding.modifiers.lazy) {
-        el.addEventListener('compositionstart', onCompositionStart)
-        el.addEventListener('compositionend', onCompositionEnd)
-        // Safari < 10.2 & UIWebView doesn't fire compositionend when
-        // switching focus before confirming composition choice
-        // this also fixes the issue where some browsers e.g. iOS Chrome
-        // fires "change" instead of "input" on autocomplete.
-        el.addEventListener('change', onCompositionEnd)
-        /* istanbul ignore if */
-        if (isIE9) {
-          el.vmodel = true
-        }
-      }
-    }
-  },
-
-  componentUpdated(el, binding, vnode) {
-    if (vnode.tag === 'select') {
-      setSelected(el, binding, vnode.context)
-      // in case the options rendered by v-for have changed,
-      // it's possible that the value is out-of-sync with the rendered options.
-      // detect such cases and filter out values that no longer has a matching
-      // option in the DOM.
-      const prevOptions = el._vOptions
-      const curOptions = (el._vOptions = [].map.call(el.options, getValue))
-      if (curOptions.some((o, i) => !looseEqual(o, prevOptions[i]))) {
-        // trigger change event if
-        // no matching option found for at least one value
-        const needReset = el.multiple
-          ? binding.value.some(v => hasNoMatchingOption(v, curOptions))
-          : binding.value !== binding.oldValue &&
-            hasNoMatchingOption(binding.value, curOptions)
-        if (needReset) {
-          trigger(el, 'change')
-        }
-      }
-    }
-  }
-}
-
-function setSelected(el, binding, vm) {
-  actuallySetSelected(el, binding, vm)
-  /* istanbul ignore if */
-  if (isIE || isEdge) {
-    setTimeout(() => {
-      actuallySetSelected(el, binding, vm)
-    }, 0)
-  }
-}
-
-function actuallySetSelected(el, binding, vm) {
-  const value = binding.value
-  const isMultiple = el.multiple
-  if (isMultiple && !Array.isArray(value)) {
-    __DEV__ &&
-      warn(
-        `<select multiple v-model="${binding.expression}"> ` +
-          `expects an Array value for its binding, but got ${Object.prototype.toString
-            .call(value)
-            .slice(8, -1)}`,
-        vm
-      )
-    return
-  }
-  let selected, option
-  for (let i = 0, l = el.options.length; i < l; i++) {
-    option = el.options[i]
-    if (isMultiple) {
-      selected = looseIndexOf(value, getValue(option)) > -1
-      if (option.selected !== selected) {
-        option.selected = selected
-      }
-    } else {
-      if (looseEqual(getValue(option), value)) {
-        if (el.selectedIndex !== i) {
-          el.selectedIndex = i
-        }
-        return
-      }
-    }
-  }
-  if (!isMultiple) {
-    el.selectedIndex = -1
-  }
-}
-
-function hasNoMatchingOption(value, options) {
-  return options.every(o => !looseEqual(o, value))
-}
-
-function getValue(option) {
-  return '_value' in option ? option._value : option.value
-}
-
-function onCompositionStart(e) {
-  e.target.composing = true
-}
-
-function onCompositionEnd(e) {
-  // prevent triggering an input event for no reason
-  if (!e.target.composing) return
-  e.target.composing = false
-  trigger(e.target, 'input')
-}
-
-function trigger(el, type) {
-  const e = document.createEvent('HTMLEvents')
-  e.initEvent(type, true, true)
-  el.dispatchEvent(e)
-}
-
-export default directive
diff --git a/src/platforms/web/runtime/directives/show.ts b/src/platforms/web/runtime/directives/show.ts
deleted file mode 100644
index e36178a0cd8..00000000000
--- a/src/platforms/web/runtime/directives/show.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import VNode from 'core/vdom/vnode'
-import type { VNodeDirective, VNodeWithData } from 'types/vnode'
-import { enter, leave } from 'web/runtime/modules/transition'
-
-// recursively search for possible transition defined inside the component root
-function locateNode(vnode: VNode | VNodeWithData): VNodeWithData {
-  // @ts-expect-error
-  return vnode.componentInstance && (!vnode.data || !vnode.data.transition)
-    ? locateNode(vnode.componentInstance._vnode!)
-    : vnode
-}
-
-export default {
-  bind(el: any, { value }: VNodeDirective, vnode: VNodeWithData) {
-    vnode = locateNode(vnode)
-    const transition = vnode.data && vnode.data.transition
-    const originalDisplay = (el.__vOriginalDisplay =
-      el.style.display === 'none' ? '' : el.style.display)
-    if (value && transition) {
-      vnode.data.show = true
-      enter(vnode, () => {
-        el.style.display = originalDisplay
-      })
-    } else {
-      el.style.display = value ? originalDisplay : 'none'
-    }
-  },
-
-  update(el: any, { value, oldValue }: VNodeDirective, vnode: VNodeWithData) {
-    /* istanbul ignore if */
-    if (!value === !oldValue) return
-    vnode = locateNode(vnode)
-    const transition = vnode.data && vnode.data.transition
-    if (transition) {
-      vnode.data.show = true
-      if (value) {
-        enter(vnode, () => {
-          el.style.display = el.__vOriginalDisplay
-        })
-      } else {
-        leave(vnode, () => {
-          el.style.display = 'none'
-        })
-      }
-    } else {
-      el.style.display = value ? el.__vOriginalDisplay : 'none'
-    }
-  },
-
-  unbind(
-    el: any,
-    binding: VNodeDirective,
-    vnode: VNodeWithData,
-    oldVnode: VNodeWithData,
-    isDestroy: boolean
-  ) {
-    if (!isDestroy) {
-      el.style.display = el.__vOriginalDisplay
-    }
-  }
-}
diff --git a/src/platforms/web/runtime/index.ts b/src/platforms/web/runtime/index.ts
deleted file mode 100644
index d8cef39291b..00000000000
--- a/src/platforms/web/runtime/index.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import Vue from 'core/index'
-import config from 'core/config'
-import { extend, noop } from 'shared/util'
-import { mountComponent } from 'core/instance/lifecycle'
-import { devtools, inBrowser } from 'core/util/index'
-
-import {
-  query,
-  mustUseProp,
-  isReservedTag,
-  isReservedAttr,
-  getTagNamespace,
-  isUnknownElement
-} from 'web/util/index'
-
-import { patch } from './patch'
-import platformDirectives from './directives/index'
-import platformComponents from './components/index'
-import type { Component } from 'types/component'
-
-// install platform specific utils
-Vue.config.mustUseProp = mustUseProp
-Vue.config.isReservedTag = isReservedTag
-Vue.config.isReservedAttr = isReservedAttr
-Vue.config.getTagNamespace = getTagNamespace
-Vue.config.isUnknownElement = isUnknownElement
-
-// install platform runtime directives & components
-extend(Vue.options.directives, platformDirectives)
-extend(Vue.options.components, platformComponents)
-
-// install platform patch function
-Vue.prototype.__patch__ = inBrowser ? patch : noop
-
-// public mount method
-Vue.prototype.$mount = function (
-  el?: string | Element,
-  hydrating?: boolean
-): Component {
-  el = el && inBrowser ? query(el) : undefined
-  return mountComponent(this, el, hydrating)
-}
-
-// devtools global hook
-/* istanbul ignore next */
-if (inBrowser) {
-  setTimeout(() => {
-    if (config.devtools) {
-      if (devtools) {
-        devtools.emit('init', Vue)
-      } else if (__DEV__ && process.env.NODE_ENV !== 'test') {
-        // @ts-expect-error
-        console[console.info ? 'info' : 'log'](
-          'Download the Vue Devtools extension for a better development experience:\n' +
-            'https://github.com/vuejs/vue-devtools'
-        )
-      }
-    }
-    if (
-      __DEV__ &&
-      process.env.NODE_ENV !== 'test' &&
-      config.productionTip !== false &&
-      typeof console !== 'undefined'
-    ) {
-      // @ts-expect-error
-      console[console.info ? 'info' : 'log'](
-        `You are running Vue in development mode.\n` +
-          `Make sure to turn on production mode when deploying for production.\n` +
-          `See more tips at https://vuejs.org/guide/deployment.html`
-      )
-    }
-  }, 0)
-}
-
-export default Vue
diff --git a/src/platforms/web/runtime/modules/attrs.ts b/src/platforms/web/runtime/modules/attrs.ts
deleted file mode 100644
index 431dc51ef63..00000000000
--- a/src/platforms/web/runtime/modules/attrs.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import { isIE, isIE9, isEdge } from 'core/util/env'
-
-import { extend, isDef, isUndef, isTrue } from 'shared/util'
-import type { VNodeWithData } from 'types/vnode'
-
-import {
-  isXlink,
-  xlinkNS,
-  getXlinkProp,
-  isBooleanAttr,
-  isEnumeratedAttr,
-  isFalsyAttrValue,
-  convertEnumeratedValue
-} from 'web/util/index'
-
-function updateAttrs(oldVnode: VNodeWithData, vnode: VNodeWithData) {
-  const opts = vnode.componentOptions
-  if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) {
-    return
-  }
-  if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) {
-    return
-  }
-  let key, cur, old
-  const elm = vnode.elm
-  const oldAttrs = oldVnode.data.attrs || {}
-  let attrs: any = vnode.data.attrs || {}
-  // clone observed objects, as the user probably wants to mutate it
-  if (isDef(attrs.__ob__) || isTrue(attrs._v_attr_proxy)) {
-    attrs = vnode.data.attrs = extend({}, attrs)
-  }
-
-  for (key in attrs) {
-    cur = attrs[key]
-    old = oldAttrs[key]
-    if (old !== cur) {
-      setAttr(elm, key, cur, vnode.data.pre)
-    }
-  }
-  // #4391: in IE9, setting type can reset value for input[type=radio]
-  // #6666: IE/Edge forces progress value down to 1 before setting a max
-  /* istanbul ignore if */
-  if ((isIE || isEdge) && attrs.value !== oldAttrs.value) {
-    setAttr(elm, 'value', attrs.value)
-  }
-  for (key in oldAttrs) {
-    if (isUndef(attrs[key])) {
-      if (isXlink(key)) {
-        elm.removeAttributeNS(xlinkNS, getXlinkProp(key))
-      } else if (!isEnumeratedAttr(key)) {
-        elm.removeAttribute(key)
-      }
-    }
-  }
-}
-
-function setAttr(el: Element, key: string, value: any, isInPre?: any) {
-  if (isInPre || el.tagName.indexOf('-') > -1) {
-    baseSetAttr(el, key, value)
-  } else if (isBooleanAttr(key)) {
-    // set attribute for blank value
-    // e.g. <option disabled>Select one</option>
-    if (isFalsyAttrValue(value)) {
-      el.removeAttribute(key)
-    } else {
-      // technically allowfullscreen is a boolean attribute for <iframe>,
-      // but Flash expects a value of "true" when used on <embed> tag
-      value = key === 'allowfullscreen' && el.tagName === 'EMBED' ? 'true' : key
-      el.setAttribute(key, value)
-    }
-  } else if (isEnumeratedAttr(key)) {
-    el.setAttribute(key, convertEnumeratedValue(key, value))
-  } else if (isXlink(key)) {
-    if (isFalsyAttrValue(value)) {
-      el.removeAttributeNS(xlinkNS, getXlinkProp(key))
-    } else {
-      el.setAttributeNS(xlinkNS, key, value)
-    }
-  } else {
-    baseSetAttr(el, key, value)
-  }
-}
-
-function baseSetAttr(el, key, value) {
-  if (isFalsyAttrValue(value)) {
-    el.removeAttribute(key)
-  } else {
-    // #7138: IE10 & 11 fires input event when setting placeholder on
-    // <textarea>... block the first input event and remove the blocker
-    // immediately.
-    /* istanbul ignore if */
-    if (
-      isIE &&
-      !isIE9 &&
-      el.tagName === 'TEXTAREA' &&
-      key === 'placeholder' &&
-      value !== '' &&
-      !el.__ieph
-    ) {
-      const blocker = e => {
-        e.stopImmediatePropagation()
-        el.removeEventListener('input', blocker)
-      }
-      el.addEventListener('input', blocker)
-      // $flow-disable-line
-      el.__ieph = true /* IE placeholder patched */
-    }
-    el.setAttribute(key, value)
-  }
-}
-
-export default {
-  create: updateAttrs,
-  update: updateAttrs
-}
diff --git a/src/platforms/web/runtime/modules/class.ts b/src/platforms/web/runtime/modules/class.ts
deleted file mode 100644
index d1720c33d5f..00000000000
--- a/src/platforms/web/runtime/modules/class.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { isDef, isUndef } from 'shared/util'
-import type { VNodeData } from 'types/vnode'
-
-import { concat, stringifyClass, genClassForVnode } from 'web/util/index'
-
-function updateClass(oldVnode: any, vnode: any) {
-  const el = vnode.elm
-  const data: VNodeData = vnode.data
-  const oldData: VNodeData = oldVnode.data
-  if (
-    isUndef(data.staticClass) &&
-    isUndef(data.class) &&
-    (isUndef(oldData) ||
-      (isUndef(oldData.staticClass) && isUndef(oldData.class)))
-  ) {
-    return
-  }
-
-  let cls = genClassForVnode(vnode)
-
-  // handle transition classes
-  const transitionClass = el._transitionClasses
-  if (isDef(transitionClass)) {
-    cls = concat(cls, stringifyClass(transitionClass))
-  }
-
-  // set the class
-  if (cls !== el._prevClass) {
-    el.setAttribute('class', cls)
-    el._prevClass = cls
-  }
-}
-
-export default {
-  create: updateClass,
-  update: updateClass
-}
diff --git a/src/platforms/web/runtime/modules/dom-props.ts b/src/platforms/web/runtime/modules/dom-props.ts
deleted file mode 100644
index b182211b9af..00000000000
--- a/src/platforms/web/runtime/modules/dom-props.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-import { isDef, isUndef, extend, toNumber, isTrue } from 'shared/util'
-import type { VNodeWithData } from 'types/vnode'
-import { isSVG } from 'web/util/index'
-
-let svgContainer
-
-function updateDOMProps(oldVnode: VNodeWithData, vnode: VNodeWithData) {
-  if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) {
-    return
-  }
-  let key, cur
-  const elm: any = vnode.elm
-  const oldProps = oldVnode.data.domProps || {}
-  let props = vnode.data.domProps || {}
-  // clone observed objects, as the user probably wants to mutate it
-  if (isDef(props.__ob__) || isTrue(props._v_attr_proxy)) {
-    props = vnode.data.domProps = extend({}, props)
-  }
-
-  for (key in oldProps) {
-    if (!(key in props)) {
-      elm[key] = ''
-    }
-  }
-
-  for (key in props) {
-    cur = props[key]
-    // ignore children if the node has textContent or innerHTML,
-    // as these will throw away existing DOM nodes and cause removal errors
-    // on subsequent patches (#3360)
-    if (key === 'textContent' || key === 'innerHTML') {
-      if (vnode.children) vnode.children.length = 0
-      if (cur === oldProps[key]) continue
-      // #6601 work around Chrome version <= 55 bug where single textNode
-      // replaced by innerHTML/textContent retains its parentNode property
-      if (elm.childNodes.length === 1) {
-        elm.removeChild(elm.childNodes[0])
-      }
-    }
-
-    if (key === 'value' && elm.tagName !== 'PROGRESS') {
-      // store value as _value as well since
-      // non-string values will be stringified
-      elm._value = cur
-      // avoid resetting cursor position when value is the same
-      const strCur = isUndef(cur) ? '' : String(cur)
-      if (shouldUpdateValue(elm, strCur)) {
-        elm.value = strCur
-      }
-    } else if (
-      key === 'innerHTML' &&
-      isSVG(elm.tagName) &&
-      isUndef(elm.innerHTML)
-    ) {
-      // IE doesn't support innerHTML for SVG elements
-      svgContainer = svgContainer || document.createElement('div')
-      svgContainer.innerHTML = `<svg>${cur}</svg>`
-      const svg = svgContainer.firstChild
-      while (elm.firstChild) {
-        elm.removeChild(elm.firstChild)
-      }
-      while (svg.firstChild) {
-        elm.appendChild(svg.firstChild)
-      }
-    } else if (
-      // skip the update if old and new VDOM state is the same.
-      // `value` is handled separately because the DOM value may be temporarily
-      // out of sync with VDOM state due to focus, composition and modifiers.
-      // This  #4521 by skipping the unnecessary `checked` update.
-      cur !== oldProps[key]
-    ) {
-      // some property updates can throw
-      // e.g. `value` on <progress> w/ non-finite value
-      try {
-        elm[key] = cur
-      } catch (e: any) {}
-    }
-  }
-}
-
-// check platforms/web/util/attrs.js acceptValue
-type acceptValueElm = HTMLInputElement | HTMLSelectElement | HTMLOptionElement
-
-function shouldUpdateValue(elm: acceptValueElm, checkVal: string): boolean {
-  return (
-    //@ts-expect-error
-    !elm.composing &&
-    (elm.tagName === 'OPTION' ||
-      isNotInFocusAndDirty(elm, checkVal) ||
-      isDirtyWithModifiers(elm, checkVal))
-  )
-}
-
-function isNotInFocusAndDirty(elm: acceptValueElm, checkVal: string): boolean {
-  // return true when textbox (.number and .trim) loses focus and its value is
-  // not equal to the updated value
-  let notInFocus = true
-  // #6157
-  // work around IE bug when accessing document.activeElement in an iframe
-  try {
-    notInFocus = document.activeElement !== elm
-  } catch (e: any) {}
-  return notInFocus && elm.value !== checkVal
-}
-
-function isDirtyWithModifiers(elm: any, newVal: string): boolean {
-  const value = elm.value
-  const modifiers = elm._vModifiers // injected by v-model runtime
-  if (isDef(modifiers)) {
-    if (modifiers.number) {
-      return toNumber(value) !== toNumber(newVal)
-    }
-    if (modifiers.trim) {
-      return value.trim() !== newVal.trim()
-    }
-  }
-  return value !== newVal
-}
-
-export default {
-  create: updateDOMProps,
-  update: updateDOMProps
-}
diff --git a/src/platforms/web/runtime/modules/events.ts b/src/platforms/web/runtime/modules/events.ts
deleted file mode 100644
index ee71333ec03..00000000000
--- a/src/platforms/web/runtime/modules/events.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-import { isDef, isUndef } from 'shared/util'
-import { updateListeners } from 'core/vdom/helpers/index'
-import { isIE, isFF, supportsPassive, isUsingMicroTask } from 'core/util/index'
-import {
-  RANGE_TOKEN,
-  CHECKBOX_RADIO_TOKEN
-} from 'web/compiler/directives/model'
-import { currentFlushTimestamp } from 'core/observer/scheduler'
-import { emptyNode } from 'core/vdom/patch'
-import type { VNodeWithData } from 'types/vnode'
-
-// normalize v-model event tokens that can only be determined at runtime.
-// it's important to place the event as the first in the array because
-// the whole point is ensuring the v-model callback gets called before
-// user-attached handlers.
-function normalizeEvents(on) {
-  /* istanbul ignore if */
-  if (isDef(on[RANGE_TOKEN])) {
-    // IE input[type=range] only supports `change` event
-    const event = isIE ? 'change' : 'input'
-    on[event] = [].concat(on[RANGE_TOKEN], on[event] || [])
-    delete on[RANGE_TOKEN]
-  }
-  // This was originally intended to fix #4521 but no longer necessary
-  // after 2.5. Keeping it for backwards compat with generated code from < 2.4
-  /* istanbul ignore if */
-  if (isDef(on[CHECKBOX_RADIO_TOKEN])) {
-    on.change = [].concat(on[CHECKBOX_RADIO_TOKEN], on.change || [])
-    delete on[CHECKBOX_RADIO_TOKEN]
-  }
-}
-
-let target: any
-
-function createOnceHandler(event, handler, capture) {
-  const _target = target // save current target element in closure
-  return function onceHandler() {
-    const res = handler.apply(null, arguments)
-    if (res !== null) {
-      remove(event, onceHandler, capture, _target)
-    }
-  }
-}
-
-// #9446: Firefox <= 53 (in particular, ESR 52) has incorrect Event.timeStamp
-// implementation and does not fire microtasks in between event propagation, so
-// safe to exclude.
-const useMicrotaskFix = isUsingMicroTask && !(isFF && Number(isFF[1]) <= 53)
-
-function add(
-  name: string,
-  handler: Function,
-  capture: boolean,
-  passive: boolean
-) {
-  // async edge case #6566: inner click event triggers patch, event handler
-  // attached to outer element during patch, and triggered again. This
-  // happens because browsers fire microtask ticks between event propagation.
-  // the solution is simple: we save the timestamp when a handler is attached,
-  // and the handler would only fire if the event passed to it was fired
-  // AFTER it was attached.
-  if (useMicrotaskFix) {
-    const attachedTimestamp = currentFlushTimestamp
-    const original = handler
-    //@ts-expect-error
-    handler = original._wrapper = function (e) {
-      if (
-        // no bubbling, should always fire.
-        // this is just a safety net in case event.timeStamp is unreliable in
-        // certain weird environments...
-        e.target === e.currentTarget ||
-        // event is fired after handler attachment
-        e.timeStamp >= attachedTimestamp ||
-        // bail for environments that have buggy event.timeStamp implementations
-        // #9462 iOS 9 bug: event.timeStamp is 0 after history.pushState
-        // #9681 QtWebEngine event.timeStamp is negative value
-        e.timeStamp <= 0 ||
-        // #9448 bail if event is fired in another document in a multi-page
-        // electron/nw.js app, since event.timeStamp will be using a different
-        // starting reference
-        e.target.ownerDocument !== document
-      ) {
-        return original.apply(this, arguments)
-      }
-    }
-  }
-  target.addEventListener(
-    name,
-    handler,
-    supportsPassive ? { capture, passive } : capture
-  )
-}
-
-function remove(
-  name: string,
-  handler: Function,
-  capture: boolean,
-  _target?: HTMLElement
-) {
-  ;(_target || target).removeEventListener(
-    name,
-    //@ts-expect-error
-    handler._wrapper || handler,
-    capture
-  )
-}
-
-function updateDOMListeners(oldVnode: VNodeWithData, vnode: VNodeWithData) {
-  if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) {
-    return
-  }
-  const on = vnode.data.on || {}
-  const oldOn = oldVnode.data.on || {}
-  // vnode is empty when removing all listeners,
-  // and use old vnode dom element
-  target = vnode.elm || oldVnode.elm
-  normalizeEvents(on)
-  updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context)
-  target = undefined
-}
-
-export default {
-  create: updateDOMListeners,
-  update: updateDOMListeners,
-  // @ts-expect-error emptyNode has actually data
-  destroy: (vnode: VNodeWithData) => updateDOMListeners(vnode, emptyNode)
-}
diff --git a/src/platforms/web/runtime/modules/index.ts b/src/platforms/web/runtime/modules/index.ts
deleted file mode 100644
index f9b21c2da2a..00000000000
--- a/src/platforms/web/runtime/modules/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import attrs from './attrs'
-import klass from './class'
-import events from './events'
-import domProps from './dom-props'
-import style from './style'
-import transition from './transition'
-
-export default [attrs, klass, events, domProps, style, transition]
diff --git a/src/platforms/web/runtime/modules/style.ts b/src/platforms/web/runtime/modules/style.ts
deleted file mode 100644
index 13898eabfd1..00000000000
--- a/src/platforms/web/runtime/modules/style.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import { getStyle, normalizeStyleBinding } from 'web/util/style'
-import {
-  cached,
-  camelize,
-  extend,
-  isDef,
-  isUndef,
-  hyphenate
-} from 'shared/util'
-import type { VNodeWithData } from 'types/vnode'
-
-const cssVarRE = /^--/
-const importantRE = /\s*!important$/
-const setProp = (el, name, val) => {
-  /* istanbul ignore if */
-  if (cssVarRE.test(name)) {
-    el.style.setProperty(name, val)
-  } else if (importantRE.test(val)) {
-    el.style.setProperty(
-      hyphenate(name),
-      val.replace(importantRE, ''),
-      'important'
-    )
-  } else {
-    const normalizedName = normalize(name)
-    if (Array.isArray(val)) {
-      // Support values array created by autoprefixer, e.g.
-      // {display: ["-webkit-box", "-ms-flexbox", "flex"]}
-      // Set them one by one, and the browser will only set those it can recognize
-      for (let i = 0, len = val.length; i < len; i++) {
-        el.style[normalizedName!] = val[i]
-      }
-    } else {
-      el.style[normalizedName!] = val
-    }
-  }
-}
-
-const vendorNames = ['Webkit', 'Moz', 'ms']
-
-let emptyStyle
-const normalize = cached(function (prop) {
-  emptyStyle = emptyStyle || document.createElement('div').style
-  prop = camelize(prop)
-  if (prop !== 'filter' && prop in emptyStyle) {
-    return prop
-  }
-  const capName = prop.charAt(0).toUpperCase() + prop.slice(1)
-  for (let i = 0; i < vendorNames.length; i++) {
-    const name = vendorNames[i] + capName
-    if (name in emptyStyle) {
-      return name
-    }
-  }
-})
-
-function updateStyle(oldVnode: VNodeWithData, vnode: VNodeWithData) {
-  const data = vnode.data
-  const oldData = oldVnode.data
-
-  if (
-    isUndef(data.staticStyle) &&
-    isUndef(data.style) &&
-    isUndef(oldData.staticStyle) &&
-    isUndef(oldData.style)
-  ) {
-    return
-  }
-
-  let cur, name
-  const el: any = vnode.elm
-  const oldStaticStyle: any = oldData.staticStyle
-  const oldStyleBinding: any = oldData.normalizedStyle || oldData.style || {}
-
-  // if static style exists, stylebinding already merged into it when doing normalizeStyleData
-  const oldStyle = oldStaticStyle || oldStyleBinding
-
-  const style = normalizeStyleBinding(vnode.data.style) || {}
-
-  // store normalized style under a different key for next diff
-  // make sure to clone it if it's reactive, since the user likely wants
-  // to mutate it.
-  vnode.data.normalizedStyle = isDef(style.__ob__) ? extend({}, style) : style
-
-  const newStyle = getStyle(vnode, true)
-
-  for (name in oldStyle) {
-    if (isUndef(newStyle[name])) {
-      setProp(el, name, '')
-    }
-  }
-  for (name in newStyle) {
-    cur = newStyle[name]
-    // ie9 setting to null has no effect, must use empty string
-    setProp(el, name, cur == null ? '' : cur)
-  }
-}
-
-export default {
-  create: updateStyle,
-  update: updateStyle
-}
diff --git a/src/platforms/web/runtime/modules/transition.ts b/src/platforms/web/runtime/modules/transition.ts
deleted file mode 100644
index ba38cf1a316..00000000000
--- a/src/platforms/web/runtime/modules/transition.ts
+++ /dev/null
@@ -1,341 +0,0 @@
-import { inBrowser, isIE9, warn } from 'core/util/index'
-import { mergeVNodeHook } from 'core/vdom/helpers/index'
-import { activeInstance } from 'core/instance/lifecycle'
-
-import {
-  once,
-  isDef,
-  isUndef,
-  isObject,
-  toNumber,
-  isFunction
-} from 'shared/util'
-
-import {
-  nextFrame,
-  resolveTransition,
-  whenTransitionEnds,
-  addTransitionClass,
-  removeTransitionClass
-} from 'web/runtime/transition-util'
-
-import type { VNodeWithData } from 'types/vnode'
-import VNode from 'core/vdom/vnode'
-
-export function enter(vnode: VNodeWithData, toggleDisplay?: () => void) {
-  const el: any = vnode.elm
-
-  // call leave callback now
-  if (isDef(el._leaveCb)) {
-    el._leaveCb.cancelled = true
-    el._leaveCb()
-  }
-
-  const data = resolveTransition(vnode.data.transition)
-  if (isUndef(data)) {
-    return
-  }
-
-  /* istanbul ignore if */
-  if (isDef(el._enterCb) || el.nodeType !== 1) {
-    return
-  }
-
-  const {
-    css,
-    type,
-    enterClass,
-    enterToClass,
-    enterActiveClass,
-    appearClass,
-    appearToClass,
-    appearActiveClass,
-    beforeEnter,
-    enter,
-    afterEnter,
-    enterCancelled,
-    beforeAppear,
-    appear,
-    afterAppear,
-    appearCancelled,
-    duration
-  } = data
-
-  // activeInstance will always be the <transition> component managing this
-  // transition. One edge case to check is when the <transition> is placed
-  // as the root node of a child component. In that case we need to check
-  // <transition>'s parent for appear check.
-  let context = activeInstance
-  let transitionNode = activeInstance.$vnode
-  while (transitionNode && transitionNode.parent) {
-    context = transitionNode.context
-    transitionNode = transitionNode.parent
-  }
-
-  const isAppear = !context._isMounted || !vnode.isRootInsert
-
-  if (isAppear && !appear && appear !== '') {
-    return
-  }
-
-  const startClass = isAppear && appearClass ? appearClass : enterClass
-  const activeClass =
-    isAppear && appearActiveClass ? appearActiveClass : enterActiveClass
-  const toClass = isAppear && appearToClass ? appearToClass : enterToClass
-
-  const beforeEnterHook = isAppear ? beforeAppear || beforeEnter : beforeEnter
-  const enterHook = isAppear ? (isFunction(appear) ? appear : enter) : enter
-  const afterEnterHook = isAppear ? afterAppear || afterEnter : afterEnter
-  const enterCancelledHook = isAppear
-    ? appearCancelled || enterCancelled
-    : enterCancelled
-
-  const explicitEnterDuration: any = toNumber(
-    isObject(duration) ? duration.enter : duration
-  )
-
-  if (__DEV__ && explicitEnterDuration != null) {
-    checkDuration(explicitEnterDuration, 'enter', vnode)
-  }
-
-  const expectsCSS = css !== false && !isIE9
-  const userWantsControl = getHookArgumentsLength(enterHook)
-
-  const cb = (el._enterCb = once(() => {
-    if (expectsCSS) {
-      removeTransitionClass(el, toClass)
-      removeTransitionClass(el, activeClass)
-    }
-    // @ts-expect-error
-    if (cb.cancelled) {
-      if (expectsCSS) {
-        removeTransitionClass(el, startClass)
-      }
-      enterCancelledHook && enterCancelledHook(el)
-    } else {
-      afterEnterHook && afterEnterHook(el)
-    }
-    el._enterCb = null
-  }))
-
-  if (!vnode.data.show) {
-    // remove pending leave element on enter by injecting an insert hook
-    mergeVNodeHook(vnode, 'insert', () => {
-      const parent = el.parentNode
-      const pendingNode =
-        parent && parent._pending && parent._pending[vnode.key!]
-      if (
-        pendingNode &&
-        pendingNode.tag === vnode.tag &&
-        pendingNode.elm._leaveCb
-      ) {
-        pendingNode.elm._leaveCb()
-      }
-      enterHook && enterHook(el, cb)
-    })
-  }
-
-  // start enter transition
-  beforeEnterHook && beforeEnterHook(el)
-  if (expectsCSS) {
-    addTransitionClass(el, startClass)
-    addTransitionClass(el, activeClass)
-    nextFrame(() => {
-      removeTransitionClass(el, startClass)
-      // @ts-expect-error
-      if (!cb.cancelled) {
-        addTransitionClass(el, toClass)
-        if (!userWantsControl) {
-          if (isValidDuration(explicitEnterDuration)) {
-            setTimeout(cb, explicitEnterDuration)
-          } else {
-            whenTransitionEnds(el, type, cb)
-          }
-        }
-      }
-    })
-  }
-
-  if (vnode.data.show) {
-    toggleDisplay && toggleDisplay()
-    enterHook && enterHook(el, cb)
-  }
-
-  if (!expectsCSS && !userWantsControl) {
-    cb()
-  }
-}
-
-export function leave(vnode: VNodeWithData, rm: Function) {
-  const el: any = vnode.elm
-
-  // call enter callback now
-  if (isDef(el._enterCb)) {
-    el._enterCb.cancelled = true
-    el._enterCb()
-  }
-
-  const data = resolveTransition(vnode.data.transition)
-  if (isUndef(data) || el.nodeType !== 1) {
-    return rm()
-  }
-
-  /* istanbul ignore if */
-  if (isDef(el._leaveCb)) {
-    return
-  }
-
-  const {
-    css,
-    type,
-    leaveClass,
-    leaveToClass,
-    leaveActiveClass,
-    beforeLeave,
-    leave,
-    afterLeave,
-    leaveCancelled,
-    delayLeave,
-    duration
-  } = data
-
-  const expectsCSS = css !== false && !isIE9
-  const userWantsControl = getHookArgumentsLength(leave)
-
-  const explicitLeaveDuration: any = toNumber(
-    isObject(duration) ? duration.leave : duration
-  )
-
-  if (__DEV__ && isDef(explicitLeaveDuration)) {
-    checkDuration(explicitLeaveDuration, 'leave', vnode)
-  }
-
-  const cb = (el._leaveCb = once(() => {
-    if (el.parentNode && el.parentNode._pending) {
-      el.parentNode._pending[vnode.key!] = null
-    }
-    if (expectsCSS) {
-      removeTransitionClass(el, leaveToClass)
-      removeTransitionClass(el, leaveActiveClass)
-    }
-    // @ts-expect-error
-    if (cb.cancelled) {
-      if (expectsCSS) {
-        removeTransitionClass(el, leaveClass)
-      }
-      leaveCancelled && leaveCancelled(el)
-    } else {
-      rm()
-      afterLeave && afterLeave(el)
-    }
-    el._leaveCb = null
-  }))
-
-  if (delayLeave) {
-    delayLeave(performLeave)
-  } else {
-    performLeave()
-  }
-
-  function performLeave() {
-    // the delayed leave may have already been cancelled
-    // @ts-expect-error
-    if (cb.cancelled) {
-      return
-    }
-    // record leaving element
-    if (!vnode.data.show && el.parentNode) {
-      ;(el.parentNode._pending || (el.parentNode._pending = {}))[vnode.key!] =
-        vnode
-    }
-    beforeLeave && beforeLeave(el)
-    if (expectsCSS) {
-      addTransitionClass(el, leaveClass)
-      addTransitionClass(el, leaveActiveClass)
-      nextFrame(() => {
-        removeTransitionClass(el, leaveClass)
-        // @ts-expect-error
-        if (!cb.cancelled) {
-          addTransitionClass(el, leaveToClass)
-          if (!userWantsControl) {
-            if (isValidDuration(explicitLeaveDuration)) {
-              setTimeout(cb, explicitLeaveDuration)
-            } else {
-              whenTransitionEnds(el, type, cb)
-            }
-          }
-        }
-      })
-    }
-    leave && leave(el, cb)
-    if (!expectsCSS && !userWantsControl) {
-      cb()
-    }
-  }
-}
-
-// only used in dev mode
-function checkDuration(val, name, vnode) {
-  if (typeof val !== 'number') {
-    warn(
-      `<transition> explicit ${name} duration is not a valid number - ` +
-        `got ${JSON.stringify(val)}.`,
-      vnode.context
-    )
-  } else if (isNaN(val)) {
-    warn(
-      `<transition> explicit ${name} duration is NaN - ` +
-        'the duration expression might be incorrect.',
-      vnode.context
-    )
-  }
-}
-
-function isValidDuration(val) {
-  return typeof val === 'number' && !isNaN(val)
-}
-
-/**
- * Normalize a transition hook's argument length. The hook may be:
- * - a merged hook (invoker) with the original in .fns
- * - a wrapped component method (check ._length)
- * - a plain function (.length)
- */
-function getHookArgumentsLength(fn: Function): boolean {
-  if (isUndef(fn)) {
-    return false
-  }
-  // @ts-expect-error
-  const invokerFns = fn.fns
-  if (isDef(invokerFns)) {
-    // invoker
-    return getHookArgumentsLength(
-      Array.isArray(invokerFns) ? invokerFns[0] : invokerFns
-    )
-  } else {
-    // @ts-expect-error
-    return (fn._length || fn.length) > 1
-  }
-}
-
-function _enter(_: any, vnode: VNodeWithData) {
-  if (vnode.data.show !== true) {
-    enter(vnode)
-  }
-}
-
-export default inBrowser
-  ? {
-      create: _enter,
-      activate: _enter,
-      remove(vnode: VNode, rm: Function) {
-        /* istanbul ignore else */
-        if (vnode.data!.show !== true) {
-          // @ts-expect-error
-          leave(vnode, rm)
-        } else {
-          rm()
-        }
-      }
-    }
-  : {}
diff --git a/src/platforms/web/runtime/node-ops.ts b/src/platforms/web/runtime/node-ops.ts
deleted file mode 100644
index fed3abc8cb2..00000000000
--- a/src/platforms/web/runtime/node-ops.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import VNode from 'core/vdom/vnode'
-import { namespaceMap } from 'web/util/index'
-
-export function createElement(tagName: string, vnode: VNode): Element {
-  const elm = document.createElement(tagName)
-  if (tagName !== 'select') {
-    return elm
-  }
-  // false or null will remove the attribute but undefined will not
-  if (
-    vnode.data &&
-    vnode.data.attrs &&
-    vnode.data.attrs.multiple !== undefined
-  ) {
-    elm.setAttribute('multiple', 'multiple')
-  }
-  return elm
-}
-
-export function createElementNS(namespace: string, tagName: string): Element {
-  return document.createElementNS(namespaceMap[namespace], tagName)
-}
-
-export function createTextNode(text: string): Text {
-  return document.createTextNode(text)
-}
-
-export function createComment(text: string): Comment {
-  return document.createComment(text)
-}
-
-export function insertBefore(
-  parentNode: Node,
-  newNode: Node,
-  referenceNode: Node
-) {
-  parentNode.insertBefore(newNode, referenceNode)
-}
-
-export function removeChild(node: Node, child: Node) {
-  node.removeChild(child)
-}
-
-export function appendChild(node: Node, child: Node) {
-  node.appendChild(child)
-}
-
-export function parentNode(node: Node) {
-  return node.parentNode
-}
-
-export function nextSibling(node: Node) {
-  return node.nextSibling
-}
-
-export function tagName(node: Element): string {
-  return node.tagName
-}
-
-export function setTextContent(node: Node, text: string) {
-  node.textContent = text
-}
-
-export function setStyleScope(node: Element, scopeId: string) {
-  node.setAttribute(scopeId, '')
-}
diff --git a/src/platforms/web/runtime/patch.ts b/src/platforms/web/runtime/patch.ts
deleted file mode 100644
index a56e67f0ce0..00000000000
--- a/src/platforms/web/runtime/patch.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import * as nodeOps from 'web/runtime/node-ops'
-import { createPatchFunction } from 'core/vdom/patch'
-import baseModules from 'core/vdom/modules/index'
-import platformModules from 'web/runtime/modules/index'
-
-// the directive module should be applied last, after all
-// built-in modules have been applied.
-const modules = platformModules.concat(baseModules)
-
-export const patch: Function = createPatchFunction({ nodeOps, modules })
diff --git a/src/platforms/web/runtime/transition-util.ts b/src/platforms/web/runtime/transition-util.ts
deleted file mode 100644
index 6174c6f37e4..00000000000
--- a/src/platforms/web/runtime/transition-util.ts
+++ /dev/null
@@ -1,215 +0,0 @@
-import { inBrowser, isIE9 } from 'core/util/index'
-import { addClass, removeClass } from 'web/runtime/class-util'
-import { remove, extend, cached } from 'shared/util'
-
-export function resolveTransition(
-  def?: string | Record<string, any>
-): Record<string, any> | undefined {
-  if (!def) {
-    return
-  }
-  /* istanbul ignore else */
-  if (typeof def === 'object') {
-    const res = {}
-    if (def.css !== false) {
-      extend(res, autoCssTransition(def.name || 'v'))
-    }
-    extend(res, def)
-    return res
-  } else if (typeof def === 'string') {
-    return autoCssTransition(def)
-  }
-}
-
-const autoCssTransition: (name: string) => Object = cached(name => {
-  return {
-    enterClass: `${name}-enter`,
-    enterToClass: `${name}-enter-to`,
-    enterActiveClass: `${name}-enter-active`,
-    leaveClass: `${name}-leave`,
-    leaveToClass: `${name}-leave-to`,
-    leaveActiveClass: `${name}-leave-active`
-  }
-})
-
-export const hasTransition = inBrowser && !isIE9
-const TRANSITION = 'transition'
-const ANIMATION = 'animation'
-
-// Transition property/event sniffing
-export let transitionProp = 'transition'
-export let transitionEndEvent = 'transitionend'
-export let animationProp = 'animation'
-export let animationEndEvent = 'animationend'
-if (hasTransition) {
-  /* istanbul ignore if */
-  if (
-    window.ontransitionend === undefined &&
-    window.onwebkittransitionend !== undefined
-  ) {
-    transitionProp = 'WebkitTransition'
-    transitionEndEvent = 'webkitTransitionEnd'
-  }
-  if (
-    window.onanimationend === undefined &&
-    window.onwebkitanimationend !== undefined
-  ) {
-    animationProp = 'WebkitAnimation'
-    animationEndEvent = 'webkitAnimationEnd'
-  }
-}
-
-// binding to window is necessary to make hot reload work in IE in strict mode
-const raf = inBrowser
-  ? window.requestAnimationFrame
-    ? window.requestAnimationFrame.bind(window)
-    : setTimeout
-  : /* istanbul ignore next */ fn => fn()
-
-export function nextFrame(fn: Function) {
-  raf(() => {
-    // @ts-expect-error
-    raf(fn)
-  })
-}
-
-export function addTransitionClass(el: any, cls: string) {
-  const transitionClasses =
-    el._transitionClasses || (el._transitionClasses = [])
-  if (transitionClasses.indexOf(cls) < 0) {
-    transitionClasses.push(cls)
-    addClass(el, cls)
-  }
-}
-
-export function removeTransitionClass(el: any, cls: string) {
-  if (el._transitionClasses) {
-    remove(el._transitionClasses, cls)
-  }
-  removeClass(el, cls)
-}
-
-export function whenTransitionEnds(
-  el: Element,
-  expectedType: string | undefined,
-  cb: Function
-) {
-  const { type, timeout, propCount } = getTransitionInfo(el, expectedType)
-  if (!type) return cb()
-  const event: string =
-    type === TRANSITION ? transitionEndEvent : animationEndEvent
-  let ended = 0
-  const end = () => {
-    el.removeEventListener(event, onEnd)
-    cb()
-  }
-  const onEnd = e => {
-    if (e.target === el) {
-      if (++ended >= propCount) {
-        end()
-      }
-    }
-  }
-  setTimeout(() => {
-    if (ended < propCount) {
-      end()
-    }
-  }, timeout + 1)
-  el.addEventListener(event, onEnd)
-}
-
-const transformRE = /\b(transform|all)(,|$)/
-
-export function getTransitionInfo(
-  el: Element,
-  expectedType?: string
-): {
-  type?: string | null
-  propCount: number
-  timeout: number
-  hasTransform: boolean
-} {
-  const styles: any = window.getComputedStyle(el)
-  // JSDOM may return undefined for transition properties
-  const transitionDelays: Array<string> = (
-    styles[transitionProp + 'Delay'] || ''
-  ).split(', ')
-  const transitionDurations: Array<string> = (
-    styles[transitionProp + 'Duration'] || ''
-  ).split(', ')
-  const transitionTimeout: number = getTimeout(
-    transitionDelays,
-    transitionDurations
-  )
-  const animationDelays: Array<string> = (
-    styles[animationProp + 'Delay'] || ''
-  ).split(', ')
-  const animationDurations: Array<string> = (
-    styles[animationProp + 'Duration'] || ''
-  ).split(', ')
-  const animationTimeout: number = getTimeout(
-    animationDelays,
-    animationDurations
-  )
-
-  let type: string | undefined | null
-  let timeout = 0
-  let propCount = 0
-  /* istanbul ignore if */
-  if (expectedType === TRANSITION) {
-    if (transitionTimeout > 0) {
-      type = TRANSITION
-      timeout = transitionTimeout
-      propCount = transitionDurations.length
-    }
-  } else if (expectedType === ANIMATION) {
-    if (animationTimeout > 0) {
-      type = ANIMATION
-      timeout = animationTimeout
-      propCount = animationDurations.length
-    }
-  } else {
-    timeout = Math.max(transitionTimeout, animationTimeout)
-    type =
-      timeout > 0
-        ? transitionTimeout > animationTimeout
-          ? TRANSITION
-          : ANIMATION
-        : null
-    propCount = type
-      ? type === TRANSITION
-        ? transitionDurations.length
-        : animationDurations.length
-      : 0
-  }
-  const hasTransform: boolean =
-    type === TRANSITION && transformRE.test(styles[transitionProp + 'Property'])
-  return {
-    type,
-    timeout,
-    propCount,
-    hasTransform
-  }
-}
-
-function getTimeout(delays: Array<string>, durations: Array<string>): number {
-  /* istanbul ignore next */
-  while (delays.length < durations.length) {
-    delays = delays.concat(delays)
-  }
-
-  return Math.max.apply(
-    null,
-    durations.map((d, i) => {
-      return toMs(d) + toMs(delays[i])
-    })
-  )
-}
-
-// Old versions of Chromium (below 61.0.3163.100) formats floating pointer numbers
-// in a locale-dependent way, using a comma instead of a dot.
-// If comma is not replaced with a dot, the input will be rounded down (i.e. acting
-// as a floor function) causing unexpected behaviors
-function toMs(s: string): number {
-  return Number(s.slice(0, -1).replace(',', '.')) * 1000
-}
diff --git a/src/platforms/web/util/attrs.ts b/src/platforms/web/util/attrs.ts
deleted file mode 100644
index e520c33d09b..00000000000
--- a/src/platforms/web/util/attrs.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { makeMap } from 'shared/util'
-
-// these are reserved for web because they are directly compiled away
-// during template compilation
-export const isReservedAttr = makeMap('style,class')
-
-// attributes that should be using props for binding
-const acceptValue = makeMap('input,textarea,option,select,progress')
-export const mustUseProp = (
-  tag: string,
-  type?: string | null,
-  attr?: string
-): boolean => {
-  return (
-    (attr === 'value' && acceptValue(tag) && type !== 'button') ||
-    (attr === 'selected' && tag === 'option') ||
-    (attr === 'checked' && tag === 'input') ||
-    (attr === 'muted' && tag === 'video')
-  )
-}
-
-export const isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck')
-
-const isValidContentEditableValue = makeMap(
-  'events,caret,typing,plaintext-only'
-)
-
-export const convertEnumeratedValue = (key: string, value: any) => {
-  return isFalsyAttrValue(value) || value === 'false'
-    ? 'false'
-    : // allow arbitrary string value for contenteditable
-    key === 'contenteditable' && isValidContentEditableValue(value)
-    ? value
-    : 'true'
-}
-
-export const isBooleanAttr = makeMap(
-  'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' +
-    'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' +
-    'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' +
-    'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' +
-    'required,reversed,scoped,seamless,selected,sortable,' +
-    'truespeed,typemustmatch,visible'
-)
-
-export const xlinkNS = 'http://www.w3.org/1999/xlink'
-
-export const isXlink = (name: string): boolean => {
-  return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink'
-}
-
-export const getXlinkProp = (name: string): string => {
-  return isXlink(name) ? name.slice(6, name.length) : ''
-}
-
-export const isFalsyAttrValue = (val: any): boolean => {
-  return val == null || val === false
-}
diff --git a/src/platforms/web/util/class.ts b/src/platforms/web/util/class.ts
deleted file mode 100644
index e2afdd71689..00000000000
--- a/src/platforms/web/util/class.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import VNode from 'core/vdom/vnode'
-import { isDef, isObject } from 'shared/util'
-import type { VNodeData, VNodeWithData } from 'types/vnode'
-
-export function genClassForVnode(vnode: VNodeWithData): string {
-  let data = vnode.data
-  let parentNode: VNode | VNodeWithData | undefined = vnode
-  let childNode: VNode | VNodeWithData = vnode
-  while (isDef(childNode.componentInstance)) {
-    childNode = childNode.componentInstance._vnode!
-    if (childNode && childNode.data) {
-      data = mergeClassData(childNode.data, data)
-    }
-  }
-  // @ts-expect-error parentNode.parent not VNodeWithData
-  while (isDef((parentNode = parentNode.parent))) {
-    if (parentNode && parentNode.data) {
-      data = mergeClassData(data, parentNode.data)
-    }
-  }
-  return renderClass(data.staticClass!, data.class)
-}
-
-function mergeClassData(
-  child: VNodeData,
-  parent: VNodeData
-): {
-  staticClass: string
-  class: any
-} {
-  return {
-    staticClass: concat(child.staticClass, parent.staticClass),
-    class: isDef(child.class) ? [child.class, parent.class] : parent.class
-  }
-}
-
-export function renderClass(
-  staticClass: string | null | undefined,
-  dynamicClass: any
-): string {
-  if (isDef(staticClass) || isDef(dynamicClass)) {
-    return concat(staticClass, stringifyClass(dynamicClass))
-  }
-  /* istanbul ignore next */
-  return ''
-}
-
-export function concat(a?: string | null, b?: string | null): string {
-  return a ? (b ? a + ' ' + b : a) : b || ''
-}
-
-export function stringifyClass(value: any): string {
-  if (Array.isArray(value)) {
-    return stringifyArray(value)
-  }
-  if (isObject(value)) {
-    return stringifyObject(value)
-  }
-  if (typeof value === 'string') {
-    return value
-  }
-  /* istanbul ignore next */
-  return ''
-}
-
-function stringifyArray(value: Array<any>): string {
-  let res = ''
-  let stringified
-  for (let i = 0, l = value.length; i < l; i++) {
-    if (isDef((stringified = stringifyClass(value[i]))) && stringified !== '') {
-      if (res) res += ' '
-      res += stringified
-    }
-  }
-  return res
-}
-
-function stringifyObject(value: Object): string {
-  let res = ''
-  for (const key in value) {
-    if (value[key]) {
-      if (res) res += ' '
-      res += key
-    }
-  }
-  return res
-}
diff --git a/src/platforms/web/util/compat.ts b/src/platforms/web/util/compat.ts
deleted file mode 100644
index eaa0b87aaa9..00000000000
--- a/src/platforms/web/util/compat.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { inBrowser } from 'core/util/index'
-
-// check whether current browser encodes a char inside attribute values
-let div
-function getShouldDecode(href: boolean): boolean {
-  div = div || document.createElement('div')
-  div.innerHTML = href ? `<a href="\n"/>` : `<div a="\n"/>`
-  return div.innerHTML.indexOf('&#10;') > 0
-}
-
-// #3663: IE encodes newlines inside attribute values while other browsers don't
-export const shouldDecodeNewlines = inBrowser ? getShouldDecode(false) : false
-// #6828: chrome encodes content in a[href]
-export const shouldDecodeNewlinesForHref = inBrowser
-  ? getShouldDecode(true)
-  : false
diff --git a/src/platforms/web/util/element.ts b/src/platforms/web/util/element.ts
deleted file mode 100644
index f1df261a355..00000000000
--- a/src/platforms/web/util/element.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import { inBrowser } from 'core/util/env'
-import { makeMap } from 'shared/util'
-
-export const namespaceMap = {
-  svg: 'http://www.w3.org/2000/svg',
-  math: 'http://www.w3.org/1998/Math/MathML'
-}
-
-export const isHTMLTag = makeMap(
-  'html,body,base,head,link,meta,style,title,' +
-    'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +
-    'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +
-    'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +
-    's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +
-    'embed,object,param,source,canvas,script,noscript,del,ins,' +
-    'caption,col,colgroup,table,thead,tbody,td,th,tr,' +
-    'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +
-    'output,progress,select,textarea,' +
-    'details,dialog,menu,menuitem,summary,' +
-    'content,element,shadow,template,blockquote,iframe,tfoot'
-)
-
-// this map is intentionally selective, only covering SVG elements that may
-// contain child elements.
-export const isSVG = makeMap(
-  'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +
-    'foreignobject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +
-    'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',
-  true
-)
-
-export const isPreTag = (tag?: string): boolean => tag === 'pre'
-
-export const isReservedTag = (tag: string): boolean | undefined => {
-  return isHTMLTag(tag) || isSVG(tag)
-}
-
-export function getTagNamespace(tag: string): string | undefined {
-  if (isSVG(tag)) {
-    return 'svg'
-  }
-  // basic support for MathML
-  // note it doesn't support other MathML elements being component roots
-  if (tag === 'math') {
-    return 'math'
-  }
-}
-
-const unknownElementCache = Object.create(null)
-export function isUnknownElement(tag: string): boolean {
-  /* istanbul ignore if */
-  if (!inBrowser) {
-    return true
-  }
-  if (isReservedTag(tag)) {
-    return false
-  }
-  tag = tag.toLowerCase()
-  /* istanbul ignore if */
-  if (unknownElementCache[tag] != null) {
-    return unknownElementCache[tag]
-  }
-  const el = document.createElement(tag)
-  if (tag.indexOf('-') > -1) {
-    // https://stackoverflow.com/a/28210364/1070244
-    return (unknownElementCache[tag] =
-      el.constructor === window.HTMLUnknownElement ||
-      el.constructor === window.HTMLElement)
-  } else {
-    return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString()))
-  }
-}
-
-export const isTextInputType = makeMap(
-  'text,number,password,search,email,tel,url'
-)
diff --git a/src/platforms/web/util/index.ts b/src/platforms/web/util/index.ts
deleted file mode 100644
index e02a0dc955e..00000000000
--- a/src/platforms/web/util/index.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { warn } from 'core/util/index'
-
-export * from './attrs'
-export * from './class'
-export * from './element'
-
-/**
- * Query an element selector if it's not an element already.
- */
-export function query(el: string | Element): Element {
-  if (typeof el === 'string') {
-    const selected = document.querySelector(el)
-    if (!selected) {
-      __DEV__ && warn('Cannot find element: ' + el)
-      return document.createElement('div')
-    }
-    return selected
-  } else {
-    return el
-  }
-}
diff --git a/src/platforms/web/util/style.ts b/src/platforms/web/util/style.ts
deleted file mode 100644
index 25971ce3f10..00000000000
--- a/src/platforms/web/util/style.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import VNode from 'core/vdom/vnode'
-import { cached, extend, toObject } from 'shared/util'
-import type { VNodeData, VNodeWithData } from 'types/vnode'
-
-export const parseStyleText = cached(function (cssText) {
-  const res = {}
-  const listDelimiter = /;(?![^(]*\))/g
-  const propertyDelimiter = /:(.+)/
-  cssText.split(listDelimiter).forEach(function (item) {
-    if (item) {
-      const tmp = item.split(propertyDelimiter)
-      tmp.length > 1 && (res[tmp[0].trim()] = tmp[1].trim())
-    }
-  })
-  return res
-})
-
-// merge static and dynamic style data on the same vnode
-function normalizeStyleData(data: VNodeData): Record<string, any> {
-  const style = normalizeStyleBinding(data.style)
-  // static style is pre-processed into an object during compilation
-  // and is always a fresh object, so it's safe to merge into it
-  return data.staticStyle ? extend(data.staticStyle, style) : style
-}
-
-// normalize possible array / string values into Object
-export function normalizeStyleBinding(bindingStyle: any): Record<string, any> {
-  if (Array.isArray(bindingStyle)) {
-    return toObject(bindingStyle)
-  }
-  if (typeof bindingStyle === 'string') {
-    return parseStyleText(bindingStyle)
-  }
-  return bindingStyle
-}
-
-/**
- * parent component style should be after child's
- * so that parent component's style could override it
- */
-export function getStyle(vnode: VNodeWithData, checkChild: boolean): Object {
-  const res = {}
-  let styleData
-
-  if (checkChild) {
-    let childNode: VNodeWithData | VNode = vnode
-    while (childNode.componentInstance) {
-      childNode = childNode.componentInstance._vnode!
-      if (
-        childNode &&
-        childNode.data &&
-        (styleData = normalizeStyleData(childNode.data))
-      ) {
-        extend(res, styleData)
-      }
-    }
-  }
-
-  if ((styleData = normalizeStyleData(vnode.data))) {
-    extend(res, styleData)
-  }
-
-  let parentNode: VNodeWithData | VNode | undefined = vnode
-  // @ts-expect-error parentNode.parent not VNodeWithData
-  while ((parentNode = parentNode.parent)) {
-    if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) {
-      extend(res, styleData)
-    }
-  }
-  return res
-}
diff --git a/src/shared/constants.ts b/src/shared/constants.ts
deleted file mode 100644
index 660c4d90519..00000000000
--- a/src/shared/constants.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-export const SSR_ATTR = 'data-server-rendered'
-
-export const ASSET_TYPES = ['component', 'directive', 'filter'] as const
-
-export const LIFECYCLE_HOOKS = [
-  'beforeCreate',
-  'created',
-  'beforeMount',
-  'mounted',
-  'beforeUpdate',
-  'updated',
-  'beforeDestroy',
-  'destroyed',
-  'activated',
-  'deactivated',
-  'errorCaptured',
-  'serverPrefetch',
-  'renderTracked',
-  'renderTriggered'
-] as const
diff --git a/src/shared/util.ts b/src/shared/util.ts
deleted file mode 100644
index 6d84877b74d..00000000000
--- a/src/shared/util.ts
+++ /dev/null
@@ -1,378 +0,0 @@
-export const emptyObject: Record<string, any> = Object.freeze({})
-
-export const isArray = Array.isArray
-
-// These helpers produce better VM code in JS engines due to their
-// explicitness and function inlining.
-export function isUndef(v: any): v is undefined | null {
-  return v === undefined || v === null
-}
-
-export function isDef<T>(v: T): v is NonNullable<T> {
-  return v !== undefined && v !== null
-}
-
-export function isTrue(v: any): boolean {
-  return v === true
-}
-
-export function isFalse(v: any): boolean {
-  return v === false
-}
-
-/**
- * Check if value is primitive.
- */
-export function isPrimitive(value: any): boolean {
-  return (
-    typeof value === 'string' ||
-    typeof value === 'number' ||
-    // $flow-disable-line
-    typeof value === 'symbol' ||
-    typeof value === 'boolean'
-  )
-}
-
-export function isFunction(value: any): value is (...args: any[]) => any {
-  return typeof value === 'function'
-}
-
-/**
- * Quick object check - this is primarily used to tell
- * objects from primitive values when we know the value
- * is a JSON-compliant type.
- */
-export function isObject(obj: any): boolean {
-  return obj !== null && typeof obj === 'object'
-}
-
-/**
- * Get the raw type string of a value, e.g., [object Object].
- */
-const _toString = Object.prototype.toString
-
-export function toRawType(value: any): string {
-  return _toString.call(value).slice(8, -1)
-}
-
-/**
- * Strict object type check. Only returns true
- * for plain JavaScript objects.
- */
-export function isPlainObject(obj: any): boolean {
-  return _toString.call(obj) === '[object Object]'
-}
-
-export function isRegExp(v: any): v is RegExp {
-  return _toString.call(v) === '[object RegExp]'
-}
-
-/**
- * Check if val is a valid array index.
- */
-export function isValidArrayIndex(val: any): boolean {
-  const n = parseFloat(String(val))
-  return n >= 0 && Math.floor(n) === n && isFinite(val)
-}
-
-export function isPromise(val: any): val is Promise<any> {
-  return (
-    isDef(val) &&
-    typeof val.then === 'function' &&
-    typeof val.catch === 'function'
-  )
-}
-
-/**
- * Convert a value to a string that is actually rendered.
- */
-export function toString(val: any): string {
-  return val == null
-    ? ''
-    : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
-    ? JSON.stringify(val, replacer, 2)
-    : String(val)
-}
-
-function replacer(_key: string, val: any): any {
-  // avoid circular deps from v3
-  if (val && val.__v_isRef) {
-    return val.value
-  }
-  return val
-}
-
-/**
- * Convert an input value to a number for persistence.
- * If the conversion fails, return original string.
- */
-export function toNumber(val: string): number | string {
-  const n = parseFloat(val)
-  return isNaN(n) ? val : n
-}
-
-/**
- * Make a map and return a function for checking if a key
- * is in that map.
- */
-export function makeMap(
-  str: string,
-  expectsLowerCase?: boolean
-): (key: string) => true | undefined {
-  const map = Object.create(null)
-  const list: Array<string> = str.split(',')
-  for (let i = 0; i < list.length; i++) {
-    map[list[i]] = true
-  }
-  return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val]
-}
-
-/**
- * Check if a tag is a built-in tag.
- */
-export const isBuiltInTag = makeMap('slot,component', true)
-
-/**
- * Check if an attribute is a reserved attribute.
- */
-export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')
-
-/**
- * Remove an item from an array.
- */
-export function remove(arr: Array<any>, item: any): Array<any> | void {
-  const len = arr.length
-  if (len) {
-    // fast path for the only / last item
-    if (item === arr[len - 1]) {
-      arr.length = len - 1
-      return
-    }
-    const index = arr.indexOf(item)
-    if (index > -1) {
-      return arr.splice(index, 1)
-    }
-  }
-}
-
-/**
- * Check whether an object has the property.
- */
-const hasOwnProperty = Object.prototype.hasOwnProperty
-export function hasOwn(obj: Object | Array<any>, key: string): boolean {
-  return hasOwnProperty.call(obj, key)
-}
-
-/**
- * Create a cached version of a pure function.
- */
-export function cached<R>(fn: (str: string) => R): (sr: string) => R {
-  const cache: Record<string, R> = Object.create(null)
-  return function cachedFn(str: string) {
-    const hit = cache[str]
-    return hit || (cache[str] = fn(str))
-  }
-}
-
-/**
- * Camelize a hyphen-delimited string.
- */
-const camelizeRE = /-(\w)/g
-export const camelize = cached((str: string): string => {
-  return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
-})
-
-/**
- * Capitalize a string.
- */
-export const capitalize = cached((str: string): string => {
-  return str.charAt(0).toUpperCase() + str.slice(1)
-})
-
-/**
- * Hyphenate a camelCase string.
- */
-const hyphenateRE = /\B([A-Z])/g
-export const hyphenate = cached((str: string): string => {
-  return str.replace(hyphenateRE, '-$1').toLowerCase()
-})
-
-/**
- * Simple bind polyfill for environments that do not support it,
- * e.g., PhantomJS 1.x. Technically, we don't need this anymore
- * since native bind is now performant enough in most browsers.
- * But removing it would mean breaking code that was able to run in
- * PhantomJS 1.x, so this must be kept for backward compatibility.
- */
-
-/* istanbul ignore next */
-function polyfillBind(fn: Function, ctx: Object): Function {
-  function boundFn(a: any) {
-    const l = arguments.length
-    return l
-      ? l > 1
-        ? fn.apply(ctx, arguments)
-        : fn.call(ctx, a)
-      : fn.call(ctx)
-  }
-
-  boundFn._length = fn.length
-  return boundFn
-}
-
-function nativeBind(fn: Function, ctx: Object): Function {
-  return fn.bind(ctx)
-}
-
-// @ts-expect-error bind cannot be `undefined`
-export const bind = Function.prototype.bind ? nativeBind : polyfillBind
-
-/**
- * Convert an Array-like object to a real Array.
- */
-export function toArray(list: any, start?: number): Array<any> {
-  start = start || 0
-  let i = list.length - start
-  const ret: Array<any> = new Array(i)
-  while (i--) {
-    ret[i] = list[i + start]
-  }
-  return ret
-}
-
-/**
- * Mix properties into target object.
- */
-export function extend(
-  to: Record<PropertyKey, any>,
-  _from?: Record<PropertyKey, any>
-): Record<PropertyKey, any> {
-  for (const key in _from) {
-    to[key] = _from[key]
-  }
-  return to
-}
-
-/**
- * Merge an Array of Objects into a single Object.
- */
-export function toObject(arr: Array<any>): object {
-  const res = {}
-  for (let i = 0; i < arr.length; i++) {
-    if (arr[i]) {
-      extend(res, arr[i])
-    }
-  }
-  return res
-}
-
-/* eslint-disable no-unused-vars */
-
-/**
- * Perform no operation.
- * Stubbing args to make Flow happy without leaving useless transpiled code
- * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).
- */
-export function noop(a?: any, b?: any, c?: any) {}
-
-/**
- * Always return false.
- */
-export const no = (a?: any, b?: any, c?: any) => false
-
-/* eslint-enable no-unused-vars */
-
-/**
- * Return the same value.
- */
-export const identity = (_: any) => _
-
-/**
- * Generate a string containing static keys from compiler modules.
- */
-export function genStaticKeys(
-  modules: Array<{ staticKeys?: string[] } /* ModuleOptions */>
-): string {
-  return modules
-    .reduce<string[]>((keys, m) => keys.concat(m.staticKeys || []), [])
-    .join(',')
-}
-
-/**
- * Check if two values are loosely equal - that is,
- * if they are plain objects, do they have the same shape?
- */
-export function looseEqual(a: any, b: any): boolean {
-  if (a === b) return true
-  const isObjectA = isObject(a)
-  const isObjectB = isObject(b)
-  if (isObjectA && isObjectB) {
-    try {
-      const isArrayA = Array.isArray(a)
-      const isArrayB = Array.isArray(b)
-      if (isArrayA && isArrayB) {
-        return (
-          a.length === b.length &&
-          a.every((e: any, i: any) => {
-            return looseEqual(e, b[i])
-          })
-        )
-      } else if (a instanceof Date && b instanceof Date) {
-        return a.getTime() === b.getTime()
-      } else if (!isArrayA && !isArrayB) {
-        const keysA = Object.keys(a)
-        const keysB = Object.keys(b)
-        return (
-          keysA.length === keysB.length &&
-          keysA.every(key => {
-            return looseEqual(a[key], b[key])
-          })
-        )
-      } else {
-        /* istanbul ignore next */
-        return false
-      }
-    } catch (e: any) {
-      /* istanbul ignore next */
-      return false
-    }
-  } else if (!isObjectA && !isObjectB) {
-    return String(a) === String(b)
-  } else {
-    return false
-  }
-}
-
-/**
- * Return the first index at which a loosely equal value can be
- * found in the array (if value is a plain object, the array must
- * contain an object of the same shape), or -1 if it is not present.
- */
-export function looseIndexOf(arr: Array<unknown>, val: unknown): number {
-  for (let i = 0; i < arr.length; i++) {
-    if (looseEqual(arr[i], val)) return i
-  }
-  return -1
-}
-
-/**
- * Ensure a function is called only once.
- */
-export function once<T extends (...args: any[]) => any>(fn: T): T {
-  let called = false
-  return function () {
-    if (!called) {
-      called = true
-      fn.apply(this, arguments as any)
-    }
-  } as any
-}
-
-// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#polyfill
-export function hasChanged(x: unknown, y: unknown): boolean {
-  if (x === y) {
-    return x === 0 && 1 / x !== 1 / (y as number)
-  } else {
-    return x === x || y === y
-  }
-}
diff --git a/src/transition/index.js b/src/transition/index.js
new file mode 100644
index 00000000000..1da11f006eb
--- /dev/null
+++ b/src/transition/index.js
@@ -0,0 +1,83 @@
+import {
+  before,
+  remove,
+  transitionEndEvent
+} from '../util/index'
+
+/**
+ * Append with transition.
+ *
+ * @param {Element} el
+ * @param {Element} target
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+export function appendWithTransition (el, target, vm, cb) {
+  applyTransition(el, 1, function () {
+    target.appendChild(el)
+  }, vm, cb)
+}
+
+/**
+ * InsertBefore with transition.
+ *
+ * @param {Element} el
+ * @param {Element} target
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+export function beforeWithTransition (el, target, vm, cb) {
+  applyTransition(el, 1, function () {
+    before(el, target)
+  }, vm, cb)
+}
+
+/**
+ * Remove with transition.
+ *
+ * @param {Element} el
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+export function removeWithTransition (el, vm, cb) {
+  applyTransition(el, -1, function () {
+    remove(el)
+  }, vm, cb)
+}
+
+/**
+ * Apply transitions with an operation callback.
+ *
+ * @param {Element} el
+ * @param {Number} direction
+ *                  1: enter
+ *                 -1: leave
+ * @param {Function} op - the actual DOM operation
+ * @param {Vue} vm
+ * @param {Function} [cb]
+ */
+
+export function applyTransition (el, direction, op, vm, cb) {
+  var transition = el.__v_trans
+  if (
+    !transition ||
+    // skip if there are no js hooks and CSS transition is
+    // not supported
+    (!transition.hooks && !transitionEndEvent) ||
+    // skip transitions for initial compile
+    !vm._isCompiled ||
+    // if the vm is being manipulated by a parent directive
+    // during the parent's compilation phase, skip the
+    // animation.
+    (vm.$parent && !vm.$parent._isCompiled)
+  ) {
+    op()
+    if (cb) cb()
+    return
+  }
+  var action = direction > 0 ? 'enter' : 'leave'
+  transition[action](op, cb)
+}
diff --git a/src/transition/queue.js b/src/transition/queue.js
new file mode 100644
index 00000000000..10e91a0b0c8
--- /dev/null
+++ b/src/transition/queue.js
@@ -0,0 +1,36 @@
+import { nextTick } from '../util/index'
+
+let queue = []
+let queued = false
+
+/**
+ * Push a job into the queue.
+ *
+ * @param {Function} job
+ */
+
+export function pushJob (job) {
+  queue.push(job)
+  if (!queued) {
+    queued = true
+    nextTick(flush)
+  }
+}
+
+/**
+ * Flush the queue, and do one forced reflow before
+ * triggering transitions.
+ */
+
+function flush () {
+  // Force layout
+  var f = document.documentElement.offsetHeight
+  for (var i = 0; i < queue.length; i++) {
+    queue[i]()
+  }
+  queue = []
+  queued = false
+  // dummy return, so js linters don't complain about
+  // unused variable f
+  return f
+}
diff --git a/src/transition/transition.js b/src/transition/transition.js
new file mode 100644
index 00000000000..f6d3d2b9cd0
--- /dev/null
+++ b/src/transition/transition.js
@@ -0,0 +1,416 @@
+import { pushJob } from './queue'
+import {
+  on,
+  off,
+  bind,
+  addClass,
+  removeClass,
+  cancellable,
+  transitionEndEvent,
+  animationEndEvent,
+  transitionProp,
+  animationProp,
+  warn,
+  inBrowser
+} from '../util/index'
+
+const TYPE_TRANSITION = 'transition'
+const TYPE_ANIMATION = 'animation'
+const transDurationProp = transitionProp + 'Duration'
+const animDurationProp = animationProp + 'Duration'
+
+/**
+ * If a just-entered element is applied the
+ * leave class while its enter transition hasn't started yet,
+ * and the transitioned property has the same value for both
+ * enter/leave, then the leave transition will be skipped and
+ * the transitionend event never fires. This function ensures
+ * its callback to be called after a transition has started
+ * by waiting for double raf.
+ *
+ * It falls back to setTimeout on devices that support CSS
+ * transitions but not raf (e.g. Android 4.2 browser) - since
+ * these environments are usually slow, we are giving it a
+ * relatively large timeout.
+ *
+ * Binding to window is necessary to make hot reload work in
+ * IE in strict mode
+ */
+
+const raf = inBrowser && window.requestAnimationFrame
+  ? window.requestAnimationFrame.bind(window)
+  : setTimeout
+
+function waitForTransitionStart (fn) {
+  raf(() => {
+    raf(fn)
+  })
+}
+
+/**
+ * A Transition object that encapsulates the state and logic
+ * of the transition.
+ *
+ * @param {Element} el
+ * @param {String} id
+ * @param {Object} hooks
+ * @param {Vue} vm
+ */
+
+export default function Transition (el, id, hooks, vm) {
+  this.id = id
+  this.el = el
+  this.enterClass = (hooks && hooks.enterClass) || id + '-enter'
+  this.leaveClass = (hooks && hooks.leaveClass) || id + '-leave'
+  this.hooks = hooks
+  this.vm = vm
+  // async state
+  this.pendingCssEvent =
+  this.pendingCssCb =
+  this.cancel =
+  this.pendingJsCb =
+  this.op =
+  this.cb = null
+  this.justEntered = false
+  this.entered = this.left = false
+  this.typeCache = {}
+  // check css transition type
+  this.type = hooks && hooks.type
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV !== 'production') {
+    if (
+      this.type &&
+      this.type !== TYPE_TRANSITION &&
+      this.type !== TYPE_ANIMATION
+    ) {
+      warn(
+        'invalid CSS transition type for transition="' +
+        this.id + '": ' + this.type,
+        vm
+      )
+    }
+  }
+  // bind
+  var self = this
+  ;['enterNextTick', 'enterDone', 'leaveNextTick', 'leaveDone']
+    .forEach(function (m) {
+      self[m] = bind(self[m], self)
+    })
+}
+
+var p = Transition.prototype
+
+/**
+ * Start an entering transition.
+ *
+ * 1. enter transition triggered
+ * 2. call beforeEnter hook
+ * 3. add enter class
+ * 4. insert/show element
+ * 5. call enter hook (with possible explicit js callback)
+ * 6. reflow
+ * 7. based on transition type:
+ *    - transition:
+ *        remove class now, wait for transitionend,
+ *        then done if there's no explicit js callback.
+ *    - animation:
+ *        wait for animationend, remove class,
+ *        then done if there's no explicit js callback.
+ *    - no css transition:
+ *        done now if there's no explicit js callback.
+ * 8. wait for either done or js callback, then call
+ *    afterEnter hook.
+ *
+ * @param {Function} op - insert/show the element
+ * @param {Function} [cb]
+ */
+
+p.enter = function (op, cb) {
+  this.cancelPending()
+  this.callHook('beforeEnter')
+  this.cb = cb
+  addClass(this.el, this.enterClass)
+  op()
+  this.entered = false
+  this.callHookWithCb('enter')
+  if (this.entered) {
+    return // user called done synchronously.
+  }
+  this.cancel = this.hooks && this.hooks.enterCancelled
+  pushJob(this.enterNextTick)
+}
+
+/**
+ * The "nextTick" phase of an entering transition, which is
+ * to be pushed into a queue and executed after a reflow so
+ * that removing the class can trigger a CSS transition.
+ */
+
+p.enterNextTick = function () {
+  // prevent transition skipping
+  this.justEntered = true
+  waitForTransitionStart(() => {
+    this.justEntered = false
+  })
+  var enterDone = this.enterDone
+  var type = this.getCssTransitionType(this.enterClass)
+  if (!this.pendingJsCb) {
+    if (type === TYPE_TRANSITION) {
+      // trigger transition by removing enter class now
+      removeClass(this.el, this.enterClass)
+      this.setupCssCb(transitionEndEvent, enterDone)
+    } else if (type === TYPE_ANIMATION) {
+      this.setupCssCb(animationEndEvent, enterDone)
+    } else {
+      enterDone()
+    }
+  } else if (type === TYPE_TRANSITION) {
+    removeClass(this.el, this.enterClass)
+  }
+}
+
+/**
+ * The "cleanup" phase of an entering transition.
+ */
+
+p.enterDone = function () {
+  this.entered = true
+  this.cancel = this.pendingJsCb = null
+  removeClass(this.el, this.enterClass)
+  this.callHook('afterEnter')
+  if (this.cb) this.cb()
+}
+
+/**
+ * Start a leaving transition.
+ *
+ * 1. leave transition triggered.
+ * 2. call beforeLeave hook
+ * 3. add leave class (trigger css transition)
+ * 4. call leave hook (with possible explicit js callback)
+ * 5. reflow if no explicit js callback is provided
+ * 6. based on transition type:
+ *    - transition or animation:
+ *        wait for end event, remove class, then done if
+ *        there's no explicit js callback.
+ *    - no css transition:
+ *        done if there's no explicit js callback.
+ * 7. wait for either done or js callback, then call
+ *    afterLeave hook.
+ *
+ * @param {Function} op - remove/hide the element
+ * @param {Function} [cb]
+ */
+
+p.leave = function (op, cb) {
+  this.cancelPending()
+  this.callHook('beforeLeave')
+  this.op = op
+  this.cb = cb
+  addClass(this.el, this.leaveClass)
+  this.left = false
+  this.callHookWithCb('leave')
+  if (this.left) {
+    return // user called done synchronously.
+  }
+  this.cancel = this.hooks && this.hooks.leaveCancelled
+  // only need to handle leaveDone if
+  // 1. the transition is already done (synchronously called
+  //    by the user, which causes this.op set to null)
+  // 2. there's no explicit js callback
+  if (this.op && !this.pendingJsCb) {
+    // if a CSS transition leaves immediately after enter,
+    // the transitionend event never fires. therefore we
+    // detect such cases and end the leave immediately.
+    if (this.justEntered) {
+      this.leaveDone()
+    } else {
+      pushJob(this.leaveNextTick)
+    }
+  }
+}
+
+/**
+ * The "nextTick" phase of a leaving transition.
+ */
+
+p.leaveNextTick = function () {
+  var type = this.getCssTransitionType(this.leaveClass)
+  if (type) {
+    var event = type === TYPE_TRANSITION
+      ? transitionEndEvent
+      : animationEndEvent
+    this.setupCssCb(event, this.leaveDone)
+  } else {
+    this.leaveDone()
+  }
+}
+
+/**
+ * The "cleanup" phase of a leaving transition.
+ */
+
+p.leaveDone = function () {
+  this.left = true
+  this.cancel = this.pendingJsCb = null
+  this.op()
+  removeClass(this.el, this.leaveClass)
+  this.callHook('afterLeave')
+  if (this.cb) this.cb()
+  this.op = null
+}
+
+/**
+ * Cancel any pending callbacks from a previously running
+ * but not finished transition.
+ */
+
+p.cancelPending = function () {
+  this.op = this.cb = null
+  var hasPending = false
+  if (this.pendingCssCb) {
+    hasPending = true
+    off(this.el, this.pendingCssEvent, this.pendingCssCb)
+    this.pendingCssEvent = this.pendingCssCb = null
+  }
+  if (this.pendingJsCb) {
+    hasPending = true
+    this.pendingJsCb.cancel()
+    this.pendingJsCb = null
+  }
+  if (hasPending) {
+    removeClass(this.el, this.enterClass)
+    removeClass(this.el, this.leaveClass)
+  }
+  if (this.cancel) {
+    this.cancel.call(this.vm, this.el)
+    this.cancel = null
+  }
+}
+
+/**
+ * Call a user-provided synchronous hook function.
+ *
+ * @param {String} type
+ */
+
+p.callHook = function (type) {
+  if (this.hooks && this.hooks[type]) {
+    this.hooks[type].call(this.vm, this.el)
+  }
+}
+
+/**
+ * Call a user-provided, potentially-async hook function.
+ * We check for the length of arguments to see if the hook
+ * expects a `done` callback. If true, the transition's end
+ * will be determined by when the user calls that callback;
+ * otherwise, the end is determined by the CSS transition or
+ * animation.
+ *
+ * @param {String} type
+ */
+
+p.callHookWithCb = function (type) {
+  var hook = this.hooks && this.hooks[type]
+  if (hook) {
+    if (hook.length > 1) {
+      this.pendingJsCb = cancellable(this[type + 'Done'])
+    }
+    hook.call(this.vm, this.el, this.pendingJsCb)
+  }
+}
+
+/**
+ * Get an element's transition type based on the
+ * calculated styles.
+ *
+ * @param {String} className
+ * @return {Number}
+ */
+
+p.getCssTransitionType = function (className) {
+  /* istanbul ignore if */
+  if (
+    !transitionEndEvent ||
+    // skip CSS transitions if page is not visible -
+    // this solves the issue of transitionend events not
+    // firing until the page is visible again.
+    // pageVisibility API is supported in IE10+, same as
+    // CSS transitions.
+    document.hidden ||
+    // explicit js-only transition
+    (this.hooks && this.hooks.css === false) ||
+    // element is hidden
+    isHidden(this.el)
+  ) {
+    return
+  }
+  var type = this.type || this.typeCache[className]
+  if (type) return type
+  var inlineStyles = this.el.style
+  var computedStyles = window.getComputedStyle(this.el)
+  var transDuration =
+    inlineStyles[transDurationProp] ||
+    computedStyles[transDurationProp]
+  if (transDuration && transDuration !== '0s') {
+    type = TYPE_TRANSITION
+  } else {
+    var animDuration =
+      inlineStyles[animDurationProp] ||
+      computedStyles[animDurationProp]
+    if (animDuration && animDuration !== '0s') {
+      type = TYPE_ANIMATION
+    }
+  }
+  if (type) {
+    this.typeCache[className] = type
+  }
+  return type
+}
+
+/**
+ * Setup a CSS transitionend/animationend callback.
+ *
+ * @param {String} event
+ * @param {Function} cb
+ */
+
+p.setupCssCb = function (event, cb) {
+  this.pendingCssEvent = event
+  var self = this
+  var el = this.el
+  var onEnd = this.pendingCssCb = function (e) {
+    if (e.target === el) {
+      off(el, event, onEnd)
+      self.pendingCssEvent = self.pendingCssCb = null
+      if (!self.pendingJsCb && cb) {
+        cb()
+      }
+    }
+  }
+  on(el, event, onEnd)
+}
+
+/**
+ * Check if an element is hidden - in that case we can just
+ * skip the transition alltogether.
+ *
+ * @param {Element} el
+ * @return {Boolean}
+ */
+
+function isHidden (el) {
+  if (/svg$/.test(el.namespaceURI)) {
+    // SVG elements do not have offset(Width|Height)
+    // so we need to check the client rect
+    var rect = el.getBoundingClientRect()
+    return !(rect.width || rect.height)
+  } else {
+    return !(
+      el.offsetWidth ||
+      el.offsetHeight ||
+      el.getClientRects().length
+    )
+  }
+}
diff --git a/src/types/compiler.ts b/src/types/compiler.ts
deleted file mode 100644
index 5c5059019f4..00000000000
--- a/src/types/compiler.ts
+++ /dev/null
@@ -1,207 +0,0 @@
-import { BindingMetadata } from 'sfc/types'
-
-export type CompilerOptions = {
-  warn?: Function // allow customizing warning in different environments; e.g. node
-  modules?: Array<ModuleOptions> // platform specific modules; e.g. style; class
-  directives?: { [key: string]: Function } // platform specific directives
-  staticKeys?: string // a list of AST properties to be considered static; for optimization
-  isUnaryTag?: (tag: string) => boolean | undefined // check if a tag is unary for the platform
-  canBeLeftOpenTag?: (tag: string) => boolean | undefined // check if a tag can be left opened
-  isReservedTag?: (tag: string) => boolean | undefined // check if a tag is a native for the platform
-  preserveWhitespace?: boolean // preserve whitespace between elements? (Deprecated)
-  whitespace?: 'preserve' | 'condense' // whitespace handling strategy
-  optimize?: boolean // optimize static content?
-
-  // web specific
-  mustUseProp?: (tag: string, type: string | null, name: string) => boolean // check if an attribute should be bound as a property
-  isPreTag?: (attr: string) => boolean | null // check if a tag needs to preserve whitespace
-  getTagNamespace?: (tag: string) => string | undefined // check the namespace for a tag
-  expectHTML?: boolean // only false for non-web builds
-  isFromDOM?: boolean
-  shouldDecodeTags?: boolean
-  shouldDecodeNewlines?: boolean
-  shouldDecodeNewlinesForHref?: boolean
-  outputSourceRange?: boolean
-  shouldKeepComment?: boolean
-
-  // runtime user-configurable
-  delimiters?: [string, string] // template delimiters
-  comments?: boolean // preserve comments in template
-
-  // for ssr optimization compiler
-  scopeId?: string
-
-  // SFC analyzed script bindings from `compileScript()`
-  bindings?: BindingMetadata
-}
-
-export type WarningMessage = {
-  msg: string
-  start?: number
-  end?: number
-}
-
-export type CompiledResult = {
-  ast: ASTElement | null
-  render: string
-  staticRenderFns: Array<string>
-  stringRenderFns?: Array<string>
-  errors?: Array<string | WarningMessage>
-  tips?: Array<string | WarningMessage>
-}
-
-export type ModuleOptions = {
-  // transform an AST node before any attributes are processed
-  // returning an ASTElement from pre/transforms replaces the element
-  preTransformNode?: (el: ASTElement) => ASTElement | null | void
-  // transform an AST node after built-ins like v-if, v-for are processed
-  transformNode?: (el: ASTElement) => ASTElement | null | void
-  // transform an AST node after its children have been processed
-  // cannot return replacement in postTransform because tree is already finalized
-  postTransformNode?: (el: ASTElement) => void
-  genData?: (el: ASTElement) => string // generate extra data string for an element
-  transformCode?: (el: ASTElement, code: string) => string // further transform generated code for an element
-  staticKeys?: Array<string> // AST properties to be considered static
-}
-
-export type ASTModifiers = { [key: string]: boolean }
-export type ASTIfCondition = { exp: string | null; block: ASTElement }
-export type ASTIfConditions = Array<ASTIfCondition>
-
-export type ASTAttr = {
-  name: string
-  value: any
-  dynamic?: boolean
-  start?: number
-  end?: number
-}
-
-export type ASTElementHandler = {
-  value: string
-  params?: Array<any>
-  modifiers: ASTModifiers | null
-  dynamic?: boolean
-  start?: number
-  end?: number
-}
-
-export type ASTElementHandlers = {
-  [key: string]: ASTElementHandler | Array<ASTElementHandler>
-}
-
-export type ASTDirective = {
-  name: string
-  rawName: string
-  value: string
-  arg: string | null
-  isDynamicArg: boolean
-  modifiers: ASTModifiers | null
-  start?: number
-  end?: number
-}
-
-export type ASTNode = ASTElement | ASTText | ASTExpression
-
-export type ASTElement = {
-  type: 1
-  tag: string
-  attrsList: Array<ASTAttr>
-  attrsMap: { [key: string]: any }
-  rawAttrsMap: { [key: string]: ASTAttr }
-  parent: ASTElement | void
-  children: Array<ASTNode>
-
-  start?: number
-  end?: number
-
-  processed?: true
-
-  static?: boolean
-  staticRoot?: boolean
-  staticInFor?: boolean
-  staticProcessed?: boolean
-  hasBindings?: boolean
-
-  text?: string
-  attrs?: Array<ASTAttr>
-  dynamicAttrs?: Array<ASTAttr>
-  props?: Array<ASTAttr>
-  plain?: boolean
-  pre?: true
-  ns?: string
-
-  component?: string
-  inlineTemplate?: true
-  transitionMode?: string | null
-  slotName?: string | null
-  slotTarget?: string | null
-  slotTargetDynamic?: boolean
-  slotScope?: string | null
-  scopedSlots?: { [name: string]: ASTElement }
-
-  ref?: string
-  refInFor?: boolean
-
-  if?: string
-  ifProcessed?: boolean
-  elseif?: string
-  else?: true
-  ifConditions?: ASTIfConditions
-
-  for?: string
-  forProcessed?: boolean
-  key?: string
-  alias?: string
-  iterator1?: string
-  iterator2?: string
-
-  staticClass?: string
-  classBinding?: string
-  staticStyle?: string
-  styleBinding?: string
-  events?: ASTElementHandlers
-  nativeEvents?: ASTElementHandlers
-
-  transition?: string | true
-  transitionOnAppear?: boolean
-
-  model?: {
-    value: string
-    callback: string
-    expression: string
-  }
-
-  directives?: Array<ASTDirective>
-
-  forbidden?: true
-  once?: true
-  onceProcessed?: boolean
-  wrapData?: (code: string) => string
-  wrapListeners?: (code: string) => string
-
-  // 2.4 ssr optimization
-  ssrOptimizability?: number
-}
-
-export type ASTExpression = {
-  type: 2
-  expression: string
-  text: string
-  tokens: Array<string | Object>
-  static?: boolean
-  // 2.4 ssr optimization
-  ssrOptimizability?: number
-  start?: number
-  end?: number
-}
-
-export type ASTText = {
-  type: 3
-  text: string
-  static?: boolean
-  isComment?: boolean
-  // 2.4 ssr optimization
-  ssrOptimizability?: number
-  start?: number
-  end?: number
-}
diff --git a/src/types/component.ts b/src/types/component.ts
deleted file mode 100644
index 5d16fa15f7a..00000000000
--- a/src/types/component.ts
+++ /dev/null
@@ -1,212 +0,0 @@
-import type VNode from 'core/vdom/vnode'
-import type Watcher from 'core/observer/watcher'
-import { ComponentOptions } from './options'
-import { SetupContext } from 'v3/apiSetup'
-import { ScopedSlotsData, VNodeChildren, VNodeData } from './vnode'
-import { GlobalAPI } from './global-api'
-import { EffectScope } from 'v3/reactivity/effectScope'
-
-// TODO this should be using the same as /component/
-
-/**
- * @internal
- */
-export declare class Component {
-  constructor(options?: any)
-  // constructor information
-  static cid: number
-  static options: Record<string, any>
-  // extend
-  static extend: GlobalAPI['extend']
-  static superOptions: Record<string, any>
-  static extendOptions: Record<string, any>
-  static sealedOptions: Record<string, any>
-  static super: typeof Component
-  // assets
-  static directive: GlobalAPI['directive']
-  static component: GlobalAPI['component']
-  static filter: GlobalAPI['filter']
-  // functional context constructor
-  static FunctionalRenderContext: Function
-  static mixin: GlobalAPI['mixin']
-  static use: GlobalAPI['use']
-
-  // public properties
-  $el: any // so that we can attach __vue__ to it
-  $data: Record<string, any>
-  $props: Record<string, any>
-  $options: ComponentOptions
-  $parent: Component | undefined
-  $root: Component
-  $children: Array<Component>
-  $refs: {
-    [key: string]: Component | Element | Array<Component | Element> | undefined
-  }
-  $slots: { [key: string]: Array<VNode> }
-  $scopedSlots: { [key: string]: () => VNode[] | undefined }
-  $vnode: VNode // the placeholder node for the component in parent's render tree
-  $attrs: { [key: string]: string }
-  $listeners: Record<string, Function | Array<Function>>
-  $isServer: boolean
-
-  // public methods
-  $mount: (
-    el?: Element | string,
-    hydrating?: boolean
-  ) => Component & { [key: string]: any }
-  $forceUpdate: () => void
-  $destroy: () => void
-  $set: <T>(
-    target: Record<string, any> | Array<T>,
-    key: string | number,
-    val: T
-  ) => T
-  $delete: <T>(
-    target: Record<string, any> | Array<T>,
-    key: string | number
-  ) => void
-  $watch: (
-    expOrFn: string | (() => any),
-    cb: Function,
-    options?: Record<string, any>
-  ) => Function
-  $on: (event: string | Array<string>, fn: Function) => Component
-  $once: (event: string, fn: Function) => Component
-  $off: (event?: string | Array<string>, fn?: Function) => Component
-  $emit: (event: string, ...args: Array<any>) => Component
-  $nextTick: (fn: (...args: any[]) => any) => void | Promise<any>
-  $createElement: (
-    tag?: string | Component,
-    data?: Record<string, any>,
-    children?: VNodeChildren
-  ) => VNode
-
-  // private properties
-  _uid: number | string
-  _name: string // this only exists in dev mode
-  _isVue: true
-  __v_skip: true
-  _self: Component
-  _renderProxy: Component
-  _renderContext?: Component
-  _watcher: Watcher | null
-  _scope: EffectScope
-  _computedWatchers: { [key: string]: Watcher }
-  _data: Record<string, any>
-  _props: Record<string, any>
-  _events: Record<string, any>
-  _inactive: boolean | null
-  _directInactive: boolean
-  _isMounted: boolean
-  _isDestroyed: boolean
-  _isBeingDestroyed: boolean
-  _vnode?: VNode | null // self root node
-  _staticTrees?: Array<VNode> | null // v-once cached trees
-  _hasHookEvent: boolean
-  _provided: Record<string, any>
-  // _virtualComponents?: { [key: string]: Component };
-
-  // @v3
-  _setupState?: Record<string, any>
-  _setupProxy?: Record<string, any>
-  _setupContext?: SetupContext
-  _attrsProxy?: Record<string, any>
-  _listenersProxy?: Record<string, Function | Function[]>
-  _slotsProxy?: Record<string, () => VNode[]>
-  _preWatchers?: Watcher[]
-
-  // private methods
-
-  // lifecycle
-  _init: Function
-  _mount: (el?: Element | void, hydrating?: boolean) => Component
-  _update: (vnode: VNode, hydrating?: boolean) => void
-
-  // rendering
-  _render: () => VNode
-
-  __patch__: (
-    a: Element | VNode | void | null,
-    b: VNode | null,
-    hydrating?: boolean,
-    removeOnly?: boolean,
-    parentElm?: any,
-    refElm?: any
-  ) => any
-
-  // createElement
-
-  // _c is internal that accepts `normalizationType` optimization hint
-  _c: (
-    vnode?: VNode,
-    data?: VNodeData,
-    children?: VNodeChildren,
-    normalizationType?: number
-  ) => VNode | void
-
-  // renderStatic
-  _m: (index: number, isInFor?: boolean) => VNode | VNodeChildren
-  // markOnce
-  _o: (
-    vnode: VNode | Array<VNode>,
-    index: number,
-    key: string
-  ) => VNode | VNodeChildren
-  // toString
-  _s: (value: any) => string
-  // text to VNode
-  _v: (value: string | number) => VNode
-  // toNumber
-  _n: (value: string) => number | string
-  // empty vnode
-  _e: () => VNode
-  // loose equal
-  _q: (a: any, b: any) => boolean
-  // loose indexOf
-  _i: (arr: Array<any>, val: any) => number
-  // resolveFilter
-  _f: (id: string) => Function
-  // renderList
-  _l: (val: any, render: Function) => Array<VNode> | null
-  // renderSlot
-  _t: (
-    name: string,
-    fallback?: Array<VNode>,
-    props?: Record<string, any>
-  ) => Array<VNode> | null
-  // apply v-bind object
-  _b: (
-    data: any,
-    tag: string,
-    value: any,
-    asProp: boolean,
-    isSync?: boolean
-  ) => VNodeData
-  // apply v-on object
-  _g: (data: any, value: any) => VNodeData
-  // check custom keyCode
-  _k: (
-    eventKeyCode: number,
-    key: string,
-    builtInAlias?: number | Array<number>,
-    eventKeyName?: string
-  ) => boolean | null
-  // resolve scoped slots
-  _u: (
-    scopedSlots: ScopedSlotsData,
-    res?: Record<string, any>
-  ) => { [key: string]: Function }
-
-  // SSR specific
-  _ssrNode: Function
-  _ssrList: Function
-  _ssrEscape: Function
-  _ssrAttr: Function
-  _ssrAttrs: Function
-  _ssrDOMProps: Function
-  _ssrClass: Function
-  _ssrStyle: Function
-
-  // allow dynamic method registration
-  // [key: string]: any
-}
diff --git a/src/types/global-api.ts b/src/types/global-api.ts
deleted file mode 100644
index c2ecfed5a6b..00000000000
--- a/src/types/global-api.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { Config } from 'core/config'
-import { Component } from './component'
-
-/**
- * @internal
- */
-export interface GlobalAPI {
-  // new(options?: any): Component
-  (options?: any): void
-  cid: number
-  options: Record<string, any>
-  config: Config
-  util: Object
-
-  extend: (options: typeof Component | object) => typeof Component
-  set: <T>(target: Object | Array<T>, key: string | number, value: T) => T
-  delete: <T>(target: Object | Array<T>, key: string | number) => void
-  nextTick: (fn: Function, context?: Object) => void | Promise<any>
-  use: (plugin: Function | Object) => GlobalAPI
-  mixin: (mixin: Object) => GlobalAPI
-  compile: (template: string) => {
-    render: Function
-    staticRenderFns: Array<Function>
-  }
-
-  directive: (id: string, def?: Function | Object) => Function | Object | void
-  component: (
-    id: string,
-    def?: typeof Component | Object
-  ) => typeof Component | void
-  filter: (id: string, def?: Function) => Function | void
-
-  observable: <T>(value: T) => T
-
-  // allow dynamic method registration
-  [key: string]: any
-}
diff --git a/src/types/modules.d.ts b/src/types/modules.d.ts
deleted file mode 100644
index 814033a1945..00000000000
--- a/src/types/modules.d.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-declare module 'de-indent' {
-  export default function deindent(input: string): string
-}
-
-declare namespace jasmine {
-  interface Matchers<T> {
-    toHaveBeenWarned(): void
-    toHaveBeenTipped(): void
-  }
-
-  interface ArrayLikeMatchers<T> {
-    toHaveBeenWarned(): void
-    toHaveBeenTipped(): void
-  }
-}
diff --git a/src/types/options.ts b/src/types/options.ts
deleted file mode 100644
index 27b3d7afc31..00000000000
--- a/src/types/options.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import VNode from 'core/vdom/vnode'
-import { DebuggerEvent } from 'v3/debug'
-import { SetupContext } from 'v3/apiSetup'
-import { Component } from './component'
-
-export type InternalComponentOptions = {
-  _isComponent: true
-  parent: Component
-  _parentVnode: VNode
-  render?: Function
-  staticRenderFns?: Array<Function>
-}
-
-type InjectKey = string | Symbol
-
-/**
- * @internal
- */
-export type ComponentOptions = {
-  // v3
-  setup?: (props: Record<string, any>, ctx: SetupContext) => unknown
-
-  [key: string]: any
-
-  componentId?: string
-
-  // data
-  data: object | Function | void
-  props?:
-    | string[]
-    | Record<string, Function | Array<Function> | null | PropOptions>
-  propsData?: object
-  computed?: {
-    [key: string]:
-      | Function
-      | {
-          get?: Function
-          set?: Function
-          cache?: boolean
-        }
-  }
-  methods?: { [key: string]: Function }
-  watch?: { [key: string]: Function | string }
-
-  // DOM
-  el?: string | Element
-  template?: string
-  render: (h: () => VNode) => VNode
-  renderError?: (h: () => VNode, err: Error) => VNode
-  staticRenderFns?: Array<() => VNode>
-
-  // lifecycle
-  beforeCreate?: Function
-  created?: Function
-  beforeMount?: Function
-  mounted?: Function
-  beforeUpdate?: Function
-  updated?: Function
-  activated?: Function
-  deactivated?: Function
-  beforeDestroy?: Function
-  destroyed?: Function
-  errorCaptured?: () => boolean | void
-  serverPrefetch?: Function
-  renderTracked?(e: DebuggerEvent): void
-  renderTriggerd?(e: DebuggerEvent): void
-
-  // assets
-  directives?: { [key: string]: object }
-  components?: { [key: string]: Component }
-  transitions?: { [key: string]: object }
-  filters?: { [key: string]: Function }
-
-  // context
-  provide?: Record<string | symbol, any> | (() => Record<string | symbol, any>)
-  inject?:
-    | { [key: string]: InjectKey | { from?: InjectKey; default?: any } }
-    | Array<string>
-
-  // component v-model customization
-  model?: {
-    prop?: string
-    event?: string
-  }
-
-  // misc
-  parent?: Component
-  mixins?: Array<object>
-  name?: string
-  extends?: Component | object
-  delimiters?: [string, string]
-  comments?: boolean
-  inheritAttrs?: boolean
-
-  // Legacy API
-  abstract?: any
-
-  // private
-  _isComponent?: true
-  _propKeys?: Array<string>
-  _parentVnode?: VNode
-  _parentListeners?: object | null
-  _renderChildren?: Array<VNode> | null
-  _componentTag: string | null
-  _scopeId: string | null
-  _base: typeof Component
-}
-
-export type PropOptions = {
-  type?: Function | Array<Function> | null
-  default?: any
-  required?: boolean | null
-  validator?: Function | null
-}
diff --git a/src/types/ssr.ts b/src/types/ssr.ts
deleted file mode 100644
index d667f283245..00000000000
--- a/src/types/ssr.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import VNode from 'core/vdom/vnode'
-import { Component } from './component'
-
-export type ComponentWithCacheContext = {
-  type: 'ComponentWithCache'
-  bufferIndex: number
-  buffer: Array<string>
-  key: string
-}
-
-export type ElementContext = {
-  type: 'Element'
-  children: Array<VNode>
-  rendered: number
-  endTag: string
-  total: number
-}
-
-export type ComponentContext = {
-  type: 'Component'
-  prevActive: Component
-}
-
-export type RenderState =
-  | ComponentContext
-  | ComponentWithCacheContext
-  | ElementContext
diff --git a/src/types/utils.ts b/src/types/utils.ts
deleted file mode 100644
index 6f7eeeeaba6..00000000000
--- a/src/types/utils.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-// If the type T accepts type "any", output type Y, otherwise output type N.
-// https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360
-export type IfAny<T, Y, N> = 0 extends 1 & T ? Y : N
diff --git a/src/types/vnode.ts b/src/types/vnode.ts
deleted file mode 100644
index cbe8f0aa98f..00000000000
--- a/src/types/vnode.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-import VNode from 'core/vdom/vnode'
-import { Ref } from 'v3'
-import { Component } from './component'
-import { ASTModifiers } from './compiler'
-
-/**
- * @internal
- */
-export type VNodeChildren =
-  | Array<null | VNode | string | number | VNodeChildren>
-  | string
-
-/**
- * @internal
- */
-export type VNodeComponentOptions = {
-  Ctor: typeof Component
-  propsData?: Object
-  listeners?: Record<string, Function | Function[]>
-  children?: Array<VNode>
-  tag?: string
-}
-
-/**
- * @internal
- */
-export type MountedComponentVNode = VNode & {
-  context: Component
-  componentOptions: VNodeComponentOptions
-  componentInstance: Component
-  parent: VNode
-  data: VNodeData
-}
-
-/**
- * @internal
- */
-// interface for vnodes in update modules
-export type VNodeWithData = VNode & {
-  tag: string
-  data: VNodeData
-  children: Array<VNode>
-  text: void
-  elm: any
-  ns: string | void
-  context: Component
-  key: string | number | undefined
-  parent?: VNodeWithData
-  componentOptions?: VNodeComponentOptions
-  componentInstance?: Component
-  isRootInsert: boolean
-}
-
-// // interface for vnodes in update modules
-// export type VNodeWithData = {
-//   tag: string;
-//   data: VNodeData;
-//   children: Array<VNode>;
-//   text: void;
-//   elm: any;
-//   ns: string | void;
-//   context: Component;
-//   key: string | number | undefined;
-//   parent?: VNodeWithData;
-//   componentOptions?: VNodeComponentOptions;
-//   componentInstance?: Component;
-//   isRootInsert: boolean;
-// };
-
-/**
- * @internal
- */
-export interface VNodeData {
-  key?: string | number
-  slot?: string
-  ref?: string | Ref | ((el: any) => void)
-  is?: string
-  pre?: boolean
-  tag?: string
-  staticClass?: string
-  class?: any
-  staticStyle?: { [key: string]: any }
-  style?: string | Array<Object> | Object
-  normalizedStyle?: Object
-  props?: { [key: string]: any }
-  attrs?: { [key: string]: string }
-  domProps?: { [key: string]: any }
-  hook?: { [key: string]: Function }
-  on?: { [key: string]: Function | Array<Function> }
-  nativeOn?: { [key: string]: Function | Array<Function> }
-  transition?: Object
-  show?: boolean // marker for v-show
-  inlineTemplate?: {
-    render: Function
-    staticRenderFns: Array<Function>
-  }
-  directives?: Array<VNodeDirective>
-  keepAlive?: boolean
-  scopedSlots?: { [key: string]: Function }
-  model?: {
-    value: any
-    callback: Function
-  }
-
-  [key: string]: any
-}
-
-/**
- * @internal
- */
-export type VNodeDirective = {
-  name: string
-  rawName: string
-  value?: any
-  oldValue?: any
-  arg?: string
-  oldArg?: string
-  modifiers?: ASTModifiers
-  def?: Object
-}
-
-/**
- * @internal
- */
-export type ScopedSlotsData = Array<
-  { key: string; fn: Function } | ScopedSlotsData
->
diff --git a/src/util/component.js b/src/util/component.js
new file mode 100644
index 00000000000..ca7dc060c4b
--- /dev/null
+++ b/src/util/component.js
@@ -0,0 +1,94 @@
+import { warn } from './debug'
+import { resolveAsset } from './options'
+import { getBindAttr } from './dom'
+
+export const commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer|main)$/i
+export const reservedTagRE = /^(slot|partial|component)$/i
+
+let isUnknownElement
+if (process.env.NODE_ENV !== 'production') {
+  isUnknownElement = function (el, tag) {
+    if (tag.indexOf('-') > -1) {
+      // http://stackoverflow.com/a/28210364/1070244
+      return (
+        el.constructor === window.HTMLUnknownElement ||
+        el.constructor === window.HTMLElement
+      )
+    } else {
+      return (
+        /HTMLUnknownElement/.test(el.toString()) &&
+        // Chrome returns unknown for several HTML5 elements.
+        // https://code.google.com/p/chromium/issues/detail?id=540526
+        // Firefox returns unknown for some "Interactive elements."
+        !/^(data|time|rtc|rb|details|dialog|summary)$/.test(tag)
+      )
+    }
+  }
+}
+
+/**
+ * Check if an element is a component, if yes return its
+ * component id.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Object|undefined}
+ */
+
+export function checkComponentAttr (el, options) {
+  var tag = el.tagName.toLowerCase()
+  var hasAttrs = el.hasAttributes()
+  if (!commonTagRE.test(tag) && !reservedTagRE.test(tag)) {
+    if (resolveAsset(options, 'components', tag)) {
+      return { id: tag }
+    } else {
+      var is = hasAttrs && getIsBinding(el, options)
+      if (is) {
+        return is
+      } else if (process.env.NODE_ENV !== 'production') {
+        var expectedTag =
+          options._componentNameMap &&
+          options._componentNameMap[tag]
+        if (expectedTag) {
+          warn(
+            'Unknown custom element: <' + tag + '> - ' +
+            'did you mean <' + expectedTag + '>? ' +
+            'HTML is case-insensitive, remember to use kebab-case in templates.'
+          )
+        } else if (isUnknownElement(el, tag)) {
+          warn(
+            'Unknown custom element: <' + tag + '> - did you ' +
+            'register the component correctly? For recursive components, ' +
+            'make sure to provide the "name" option.'
+          )
+        }
+      }
+    }
+  } else if (hasAttrs) {
+    return getIsBinding(el, options)
+  }
+}
+
+/**
+ * Get "is" binding from an element.
+ *
+ * @param {Element} el
+ * @param {Object} options
+ * @return {Object|undefined}
+ */
+
+function getIsBinding (el, options) {
+  // dynamic syntax
+  var exp = el.getAttribute('is')
+  if (exp != null) {
+    if (resolveAsset(options, 'components', exp)) {
+      el.removeAttribute('is')
+      return { id: exp }
+    }
+  } else {
+    exp = getBindAttr(el, 'is')
+    if (exp != null) {
+      return { id: exp, dynamic: true }
+    }
+  }
+}
diff --git a/src/util/debug.js b/src/util/debug.js
new file mode 100644
index 00000000000..275658f78d5
--- /dev/null
+++ b/src/util/debug.js
@@ -0,0 +1,24 @@
+import config from '../config'
+import { hyphenate } from './lang'
+
+let warn
+let formatComponentName
+
+if (process.env.NODE_ENV !== 'production') {
+  const hasConsole = typeof console !== 'undefined'
+
+  warn = (msg, vm) => {
+    if (hasConsole && (!config.silent)) {
+      console.error('[Vue warn]: ' + msg + (vm ? formatComponentName(vm) : ''))
+    }
+  }
+
+  formatComponentName = vm => {
+    var name = vm._isVue ? vm.$options.name : vm.name
+    return name
+      ? ' (found in component: <' + hyphenate(name) + '>)'
+      : ''
+  }
+}
+
+export { warn }
diff --git a/src/util/dom.js b/src/util/dom.js
new file mode 100644
index 00000000000..26382953e0a
--- /dev/null
+++ b/src/util/dom.js
@@ -0,0 +1,451 @@
+import config from '../config'
+import { isIE9 } from './env'
+import { warn } from './debug'
+import { camelize } from './lang'
+import { removeWithTransition } from '../transition/index'
+
+/**
+ * Query an element selector if it's not an element already.
+ *
+ * @param {String|Element} el
+ * @return {Element}
+ */
+
+export function query (el) {
+  if (typeof el === 'string') {
+    var selector = el
+    el = document.querySelector(el)
+    if (!el) {
+      process.env.NODE_ENV !== 'production' && warn(
+        'Cannot find element: ' + selector
+      )
+    }
+  }
+  return el
+}
+
+/**
+ * Check if a node is in the document.
+ * Note: document.documentElement.contains should work here
+ * but always returns false for comment nodes in phantomjs,
+ * making unit tests difficult. This is fixed by doing the
+ * contains() check on the node's parentNode instead of
+ * the node itself.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+export function inDoc (node) {
+  if (!node) return false
+  var doc = node.ownerDocument.documentElement
+  var parent = node.parentNode
+  return doc === node ||
+    doc === parent ||
+    !!(parent && parent.nodeType === 1 && (doc.contains(parent)))
+}
+
+/**
+ * Get and remove an attribute from a node.
+ *
+ * @param {Node} node
+ * @param {String} _attr
+ */
+
+export function getAttr (node, _attr) {
+  var val = node.getAttribute(_attr)
+  if (val !== null) {
+    node.removeAttribute(_attr)
+  }
+  return val
+}
+
+/**
+ * Get an attribute with colon or v-bind: prefix.
+ *
+ * @param {Node} node
+ * @param {String} name
+ * @return {String|null}
+ */
+
+export function getBindAttr (node, name) {
+  var val = getAttr(node, ':' + name)
+  if (val === null) {
+    val = getAttr(node, 'v-bind:' + name)
+  }
+  return val
+}
+
+/**
+ * Check the presence of a bind attribute.
+ *
+ * @param {Node} node
+ * @param {String} name
+ * @return {Boolean}
+ */
+
+export function hasBindAttr (node, name) {
+  return node.hasAttribute(name) ||
+    node.hasAttribute(':' + name) ||
+    node.hasAttribute('v-bind:' + name)
+}
+
+/**
+ * Insert el before target
+ *
+ * @param {Element} el
+ * @param {Element} target
+ */
+
+export function before (el, target) {
+  target.parentNode.insertBefore(el, target)
+}
+
+/**
+ * Insert el after target
+ *
+ * @param {Element} el
+ * @param {Element} target
+ */
+
+export function after (el, target) {
+  if (target.nextSibling) {
+    before(el, target.nextSibling)
+  } else {
+    target.parentNode.appendChild(el)
+  }
+}
+
+/**
+ * Remove el from DOM
+ *
+ * @param {Element} el
+ */
+
+export function remove (el) {
+  el.parentNode.removeChild(el)
+}
+
+/**
+ * Prepend el to target
+ *
+ * @param {Element} el
+ * @param {Element} target
+ */
+
+export function prepend (el, target) {
+  if (target.firstChild) {
+    before(el, target.firstChild)
+  } else {
+    target.appendChild(el)
+  }
+}
+
+/**
+ * Replace target with el
+ *
+ * @param {Element} target
+ * @param {Element} el
+ */
+
+export function replace (target, el) {
+  var parent = target.parentNode
+  if (parent) {
+    parent.replaceChild(el, target)
+  }
+}
+
+/**
+ * Add event listener shorthand.
+ *
+ * @param {Element} el
+ * @param {String} event
+ * @param {Function} cb
+ * @param {Boolean} [useCapture]
+ */
+
+export function on (el, event, cb, useCapture) {
+  el.addEventListener(event, cb, useCapture)
+}
+
+/**
+ * Remove event listener shorthand.
+ *
+ * @param {Element} el
+ * @param {String} event
+ * @param {Function} cb
+ */
+
+export function off (el, event, cb) {
+  el.removeEventListener(event, cb)
+}
+
+/**
+ * For IE9 compat: when both class and :class are present
+ * getAttribute('class') returns wrong value...
+ *
+ * @param {Element} el
+ * @return {String}
+ */
+
+function getClass (el) {
+  var classname = el.className
+  if (typeof classname === 'object') {
+    classname = classname.baseVal || ''
+  }
+  return classname
+}
+
+/**
+ * In IE9, setAttribute('class') will result in empty class
+ * if the element also has the :class attribute; However in
+ * PhantomJS, setting `className` does not work on SVG elements...
+ * So we have to do a conditional check here.
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+export function setClass (el, cls) {
+  /* istanbul ignore if */
+  if (isIE9 && !/svg$/.test(el.namespaceURI)) {
+    el.className = cls
+  } else {
+    el.setAttribute('class', cls)
+  }
+}
+
+/**
+ * Add class with compatibility for IE & SVG
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+export function addClass (el, cls) {
+  if (el.classList) {
+    el.classList.add(cls)
+  } else {
+    var cur = ' ' + getClass(el) + ' '
+    if (cur.indexOf(' ' + cls + ' ') < 0) {
+      setClass(el, (cur + cls).trim())
+    }
+  }
+}
+
+/**
+ * Remove class with compatibility for IE & SVG
+ *
+ * @param {Element} el
+ * @param {String} cls
+ */
+
+export function removeClass (el, cls) {
+  if (el.classList) {
+    el.classList.remove(cls)
+  } else {
+    var cur = ' ' + getClass(el) + ' '
+    var tar = ' ' + cls + ' '
+    while (cur.indexOf(tar) >= 0) {
+      cur = cur.replace(tar, ' ')
+    }
+    setClass(el, cur.trim())
+  }
+  if (!el.className) {
+    el.removeAttribute('class')
+  }
+}
+
+/**
+ * Extract raw content inside an element into a temporary
+ * container div
+ *
+ * @param {Element} el
+ * @param {Boolean} asFragment
+ * @return {Element|DocumentFragment}
+ */
+
+export function extractContent (el, asFragment) {
+  var child
+  var rawContent
+  /* istanbul ignore if */
+  if (isTemplate(el) && isFragment(el.content)) {
+    el = el.content
+  }
+  if (el.hasChildNodes()) {
+    trimNode(el)
+    rawContent = asFragment
+      ? document.createDocumentFragment()
+      : document.createElement('div')
+    /* eslint-disable no-cond-assign */
+    while (child = el.firstChild) {
+    /* eslint-enable no-cond-assign */
+      rawContent.appendChild(child)
+    }
+  }
+  return rawContent
+}
+
+/**
+ * Trim possible empty head/tail text and comment
+ * nodes inside a parent.
+ *
+ * @param {Node} node
+ */
+
+export function trimNode (node) {
+  var child
+  /* eslint-disable no-sequences */
+  while (child = node.firstChild, isTrimmable(child)) {
+    node.removeChild(child)
+  }
+  while (child = node.lastChild, isTrimmable(child)) {
+    node.removeChild(child)
+  }
+  /* eslint-enable no-sequences */
+}
+
+function isTrimmable (node) {
+  return node && (
+    (node.nodeType === 3 && !node.data.trim()) ||
+    node.nodeType === 8
+  )
+}
+
+/**
+ * Check if an element is a template tag.
+ * Note if the template appears inside an SVG its tagName
+ * will be in lowercase.
+ *
+ * @param {Element} el
+ */
+
+export function isTemplate (el) {
+  return el.tagName &&
+    el.tagName.toLowerCase() === 'template'
+}
+
+/**
+ * Create an "anchor" for performing dom insertion/removals.
+ * This is used in a number of scenarios:
+ * - fragment instance
+ * - v-html
+ * - v-if
+ * - v-for
+ * - component
+ *
+ * @param {String} content
+ * @param {Boolean} persist - IE trashes empty textNodes on
+ *                            cloneNode(true), so in certain
+ *                            cases the anchor needs to be
+ *                            non-empty to be persisted in
+ *                            templates.
+ * @return {Comment|Text}
+ */
+
+export function createAnchor (content, persist) {
+  var anchor = config.debug
+    ? document.createComment(content)
+    : document.createTextNode(persist ? ' ' : '')
+  anchor.__v_anchor = true
+  return anchor
+}
+
+/**
+ * Find a component ref attribute that starts with $.
+ *
+ * @param {Element} node
+ * @return {String|undefined}
+ */
+
+var refRE = /^v-ref:/
+export function findRef (node) {
+  if (node.hasAttributes()) {
+    var attrs = node.attributes
+    for (var i = 0, l = attrs.length; i < l; i++) {
+      var name = attrs[i].name
+      if (refRE.test(name)) {
+        return camelize(name.replace(refRE, ''))
+      }
+    }
+  }
+}
+
+/**
+ * Map a function to a range of nodes .
+ *
+ * @param {Node} node
+ * @param {Node} end
+ * @param {Function} op
+ */
+
+export function mapNodeRange (node, end, op) {
+  var next
+  while (node !== end) {
+    next = node.nextSibling
+    op(node)
+    node = next
+  }
+  op(end)
+}
+
+/**
+ * Remove a range of nodes with transition, store
+ * the nodes in a fragment with correct ordering,
+ * and call callback when done.
+ *
+ * @param {Node} start
+ * @param {Node} end
+ * @param {Vue} vm
+ * @param {DocumentFragment} frag
+ * @param {Function} cb
+ */
+
+export function removeNodeRange (start, end, vm, frag, cb) {
+  var done = false
+  var removed = 0
+  var nodes = []
+  mapNodeRange(start, end, function (node) {
+    if (node === end) done = true
+    nodes.push(node)
+    removeWithTransition(node, vm, onRemoved)
+  })
+  function onRemoved () {
+    removed++
+    if (done && removed >= nodes.length) {
+      for (var i = 0; i < nodes.length; i++) {
+        frag.appendChild(nodes[i])
+      }
+      cb && cb()
+    }
+  }
+}
+
+/**
+ * Check if a node is a DocumentFragment.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+export function isFragment (node) {
+  return node && node.nodeType === 11
+}
+
+/**
+ * Get outerHTML of elements, taking care
+ * of SVG elements in IE as well.
+ *
+ * @param {Element} el
+ * @return {String}
+ */
+
+export function getOuterHTML (el) {
+  if (el.outerHTML) {
+    return el.outerHTML
+  } else {
+    var container = document.createElement('div')
+    container.appendChild(el.cloneNode(true))
+    return container.innerHTML
+  }
+}
diff --git a/src/util/env.js b/src/util/env.js
new file mode 100644
index 00000000000..299707d1e45
--- /dev/null
+++ b/src/util/env.js
@@ -0,0 +1,154 @@
+/* globals MutationObserver */
+
+// can we use __proto__?
+export const hasProto = '__proto__' in {}
+
+// Browser environment sniffing
+export const inBrowser =
+  typeof window !== 'undefined' &&
+  Object.prototype.toString.call(window) !== '[object Object]'
+
+// detect devtools
+export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__
+
+// UA sniffing for working around browser-specific quirks
+const UA = inBrowser && window.navigator.userAgent.toLowerCase()
+export const isIE = UA && UA.indexOf('trident') > 0
+export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
+export const isAndroid = UA && UA.indexOf('android') > 0
+export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA)
+
+let transitionProp
+let transitionEndEvent
+let animationProp
+let animationEndEvent
+
+// Transition property/event sniffing
+if (inBrowser && !isIE9) {
+  const isWebkitTrans =
+    window.ontransitionend === undefined &&
+    window.onwebkittransitionend !== undefined
+  const isWebkitAnim =
+    window.onanimationend === undefined &&
+    window.onwebkitanimationend !== undefined
+  transitionProp = isWebkitTrans
+    ? 'WebkitTransition'
+    : 'transition'
+  transitionEndEvent = isWebkitTrans
+    ? 'webkitTransitionEnd'
+    : 'transitionend'
+  animationProp = isWebkitAnim
+    ? 'WebkitAnimation'
+    : 'animation'
+  animationEndEvent = isWebkitAnim
+    ? 'webkitAnimationEnd'
+    : 'animationend'
+}
+
+export {
+  transitionProp,
+  transitionEndEvent,
+  animationProp,
+  animationEndEvent
+}
+
+/* istanbul ignore next */
+function isNative (Ctor) {
+  return /native code/.test(Ctor.toString())
+}
+
+/**
+ * Defer a task to execute it asynchronously. Ideally this
+ * should be executed as a microtask, so we leverage
+ * MutationObserver if it's available, and fallback to
+ * setTimeout(0).
+ *
+ * @param {Function} cb
+ * @param {Object} ctx
+ */
+
+export const nextTick = (function () {
+  const callbacks = []
+  let pending = false
+  let timerFunc
+
+  function nextTickHandler () {
+    pending = false
+    const copies = callbacks.slice(0)
+    callbacks.length = 0
+    for (let i = 0; i < copies.length; i++) {
+      copies[i]()
+    }
+  }
+
+  // the nextTick behavior leverages the microtask queue, which can be accessed
+  // via either native Promise.then or MutationObserver.
+  // MutationObserver has wider support, however it is seriously bugged in
+  // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
+  // completely stops working after triggering a few times... so, if native
+  // Promise is available, we will use it:
+  /* istanbul ignore if */
+  if (typeof Promise !== 'undefined' && isNative(Promise)) {
+    var p = Promise.resolve()
+    var noop = function () {}
+    timerFunc = () => {
+      p.then(nextTickHandler)
+      // in problematic UIWebViews, Promise.then doesn't completely break, but
+      // it can get stuck in a weird state where callbacks are pushed into the
+      // microtask queue but the queue isn't being flushed, until the browser
+      // needs to do some other work, e.g. handle a timer. Therefore we can
+      // "force" the microtask queue to be flushed by adding an empty timer.
+      if (isIOS) setTimeout(noop)
+    }
+  } else if (typeof MutationObserver !== 'undefined') {
+    // use MutationObserver where native Promise is not available,
+    // e.g. IE11, iOS7, Android 4.4
+    var counter = 1
+    var observer = new MutationObserver(nextTickHandler)
+    var textNode = document.createTextNode(String(counter))
+    observer.observe(textNode, {
+      characterData: true
+    })
+    timerFunc = () => {
+      counter = (counter + 1) % 2
+      textNode.data = String(counter)
+    }
+  } else {
+    // fallback to setTimeout
+    /* istanbul ignore next */
+    timerFunc = setTimeout
+  }
+
+  return function (cb, ctx) {
+    var func = ctx
+      ? function () { cb.call(ctx) }
+      : cb
+    callbacks.push(func)
+    if (pending) return
+    pending = true
+    timerFunc(nextTickHandler, 0)
+  }
+})()
+
+let _Set
+/* istanbul ignore if */
+if (typeof Set !== 'undefined' && isNative(Set)) {
+  // use native Set when available.
+  _Set = Set
+} else {
+  // a non-standard Set polyfill that only works with primitive keys.
+  _Set = function () {
+    this.set = Object.create(null)
+  }
+  _Set.prototype.has = function (key) {
+    return this.set[key] !== undefined
+  }
+  _Set.prototype.add = function (key) {
+    this.set[key] = 1
+  }
+  _Set.prototype.clear = function () {
+    this.set = Object.create(null)
+  }
+}
+
+export { _Set }
diff --git a/src/core/util/index.ts b/src/util/index.js
similarity index 58%
rename from src/core/util/index.ts
rename to src/util/index.js
index c47b8dbe937..7352358f414 100644
--- a/src/core/util/index.ts
+++ b/src/util/index.js
@@ -1,9 +1,7 @@
-export * from 'shared/util'
 export * from './lang'
 export * from './env'
+export * from './dom'
 export * from './options'
+export * from './component'
 export * from './debug'
-export * from './props'
-export * from './error'
-export * from './next-tick'
 export { defineReactive } from '../observer/index'
diff --git a/src/util/lang.js b/src/util/lang.js
new file mode 100644
index 00000000000..cf5956ee3a1
--- /dev/null
+++ b/src/util/lang.js
@@ -0,0 +1,407 @@
+/**
+ * Set a property on an object. Adds the new property and
+ * triggers change notification if the property doesn't
+ * already exist.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {*} val
+ * @public
+ */
+
+export function set (obj, key, val) {
+  if (hasOwn(obj, key)) {
+    obj[key] = val
+    return
+  }
+  if (obj._isVue) {
+    set(obj._data, key, val)
+    return
+  }
+  var ob = obj.__ob__
+  if (!ob) {
+    obj[key] = val
+    return
+  }
+  ob.convert(key, val)
+  ob.dep.notify()
+  if (ob.vms) {
+    var i = ob.vms.length
+    while (i--) {
+      var vm = ob.vms[i]
+      vm._proxy(key)
+      vm._digest()
+    }
+  }
+  return val
+}
+
+/**
+ * Delete a property and trigger change if necessary.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ */
+
+export function del (obj, key) {
+  if (!hasOwn(obj, key)) {
+    return
+  }
+  delete obj[key]
+  var ob = obj.__ob__
+  if (!ob) {
+    if (obj._isVue) {
+      delete obj._data[key]
+      obj._digest()
+    }
+    return
+  }
+  ob.dep.notify()
+  if (ob.vms) {
+    var i = ob.vms.length
+    while (i--) {
+      var vm = ob.vms[i]
+      vm._unproxy(key)
+      vm._digest()
+    }
+  }
+}
+
+var hasOwnProperty = Object.prototype.hasOwnProperty
+/**
+ * Check whether the object has the property.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @return {Boolean}
+ */
+export function hasOwn (obj, key) {
+  return hasOwnProperty.call(obj, key)
+}
+
+/**
+ * Check if an expression is a literal value.
+ *
+ * @param {String} exp
+ * @return {Boolean}
+ */
+
+var literalValueRE = /^\s?(true|false|-?[\d\.]+|'[^']*'|"[^"]*")\s?$/
+export function isLiteral (exp) {
+  return literalValueRE.test(exp)
+}
+
+/**
+ * Check if a string starts with $ or _
+ *
+ * @param {String} str
+ * @return {Boolean}
+ */
+
+export function isReserved (str) {
+  var c = (str + '').charCodeAt(0)
+  return c === 0x24 || c === 0x5F
+}
+
+/**
+ * Guard text output, make sure undefined outputs
+ * empty string
+ *
+ * @param {*} value
+ * @return {String}
+ */
+
+export function _toString (value) {
+  return value == null
+    ? ''
+    : value.toString()
+}
+
+/**
+ * Check and convert possible numeric strings to numbers
+ * before setting back to data
+ *
+ * @param {*} value
+ * @return {*|Number}
+ */
+
+export function toNumber (value) {
+  if (typeof value !== 'string' || value.trim() === '') {
+    return value
+  }
+  var parsed = Number(value)
+  return isNaN(parsed)
+    ? value
+    : parsed
+}
+
+/**
+ * Convert string boolean literals into real booleans.
+ *
+ * @param {*} value
+ * @return {*|Boolean}
+ */
+
+export function toBoolean (value) {
+  return value === 'true'
+    ? true
+    : value === 'false'
+      ? false
+      : value
+}
+
+/**
+ * Strip quotes from a string
+ *
+ * @param {String} str
+ * @return {String | false}
+ */
+
+export function stripQuotes (str) {
+  var a = str.charCodeAt(0)
+  var b = str.charCodeAt(str.length - 1)
+  return a === b && (a === 0x22 || a === 0x27)
+    ? str.slice(1, -1)
+    : str
+}
+
+/**
+ * Camelize a hyphen-delimited string.
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+var camelizeRE = /-(\w)/g
+export function camelize (str) {
+  return str.replace(camelizeRE, toUpper)
+}
+
+function toUpper (_, c) {
+  return c ? c.toUpperCase() : ''
+}
+
+/**
+ * Hyphenate a camelCase string.
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+var hyphenateRE = /([^-])([A-Z])/g
+export function hyphenate (str) {
+  return str
+    .replace(hyphenateRE, '$1-$2')
+    .replace(hyphenateRE, '$1-$2')
+    .toLowerCase()
+}
+
+/**
+ * Converts hyphen/underscore/slash delimitered names into
+ * camelized classNames.
+ *
+ * e.g. my-component => MyComponent
+ *      some_else    => SomeElse
+ *      some/comp    => SomeComp
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+var classifyRE = /(?:^|[-_\/])(\w)/g
+export function classify (str) {
+  return str.replace(classifyRE, toUpper)
+}
+
+/**
+ * Simple bind, faster than native
+ *
+ * @param {Function} fn
+ * @param {Object} ctx
+ * @return {Function}
+ */
+
+export function bind (fn, ctx) {
+  return function (a) {
+    var l = arguments.length
+    return l
+      ? l > 1
+        ? fn.apply(ctx, arguments)
+        : fn.call(ctx, a)
+      : fn.call(ctx)
+  }
+}
+
+/**
+ * Convert an Array-like object to a real Array.
+ *
+ * @param {Array-like} list
+ * @param {Number} [start] - start index
+ * @return {Array}
+ */
+
+export function toArray (list, start) {
+  start = start || 0
+  var i = list.length - start
+  var ret = new Array(i)
+  while (i--) {
+    ret[i] = list[i + start]
+  }
+  return ret
+}
+
+/**
+ * Mix properties into target object.
+ *
+ * @param {Object} to
+ * @param {Object} from
+ */
+
+export function extend (to, from) {
+  var keys = Object.keys(from)
+  var i = keys.length
+  while (i--) {
+    to[keys[i]] = from[keys[i]]
+  }
+  return to
+}
+
+/**
+ * Quick object check - this is primarily used to tell
+ * Objects from primitive values when we know the value
+ * is a JSON-compliant type.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+export function isObject (obj) {
+  return obj !== null && typeof obj === 'object'
+}
+
+/**
+ * Strict object type check. Only returns true
+ * for plain JavaScript objects.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+var toString = Object.prototype.toString
+var OBJECT_STRING = '[object Object]'
+export function isPlainObject (obj) {
+  return toString.call(obj) === OBJECT_STRING
+}
+
+/**
+ * Array type check.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+export const isArray = Array.isArray
+
+/**
+ * Define a property.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {*} val
+ * @param {Boolean} [enumerable]
+ */
+
+export function def (obj, key, val, enumerable) {
+  Object.defineProperty(obj, key, {
+    value: val,
+    enumerable: !!enumerable,
+    writable: true,
+    configurable: true
+  })
+}
+
+/**
+ * Debounce a function so it only gets called after the
+ * input stops arriving after the given wait period.
+ *
+ * @param {Function} func
+ * @param {Number} wait
+ * @return {Function} - the debounced function
+ */
+
+export function debounce (func, wait) {
+  var timeout, args, context, timestamp, result
+  var later = function () {
+    var last = Date.now() - timestamp
+    if (last < wait && last >= 0) {
+      timeout = setTimeout(later, wait - last)
+    } else {
+      timeout = null
+      result = func.apply(context, args)
+      if (!timeout) context = args = null
+    }
+  }
+  return function () {
+    context = this
+    args = arguments
+    timestamp = Date.now()
+    if (!timeout) {
+      timeout = setTimeout(later, wait)
+    }
+    return result
+  }
+}
+
+/**
+ * Manual indexOf because it's slightly faster than
+ * native.
+ *
+ * @param {Array} arr
+ * @param {*} obj
+ */
+
+export function indexOf (arr, obj) {
+  var i = arr.length
+  while (i--) {
+    if (arr[i] === obj) return i
+  }
+  return -1
+}
+
+/**
+ * Make a cancellable version of an async callback.
+ *
+ * @param {Function} fn
+ * @return {Function}
+ */
+
+export function cancellable (fn) {
+  var cb = function () {
+    if (!cb.cancelled) {
+      return fn.apply(this, arguments)
+    }
+  }
+  cb.cancel = function () {
+    cb.cancelled = true
+  }
+  return cb
+}
+
+/**
+ * Check if two values are loosely equal - that is,
+ * if they are plain objects, do they have the same shape?
+ *
+ * @param {*} a
+ * @param {*} b
+ * @return {Boolean}
+ */
+
+export function looseEqual (a, b) {
+  /* eslint-disable eqeqeq */
+  return a == b || (
+    isObject(a) && isObject(b)
+      ? JSON.stringify(a) === JSON.stringify(b)
+      : false
+  )
+  /* eslint-enable eqeqeq */
+}
diff --git a/src/util/options.js b/src/util/options.js
new file mode 100644
index 00000000000..49f135d6c0d
--- /dev/null
+++ b/src/util/options.js
@@ -0,0 +1,395 @@
+import Vue from '../instance/vue'
+import config from '../config'
+import {
+  extend,
+  set,
+  isObject,
+  isArray,
+  isPlainObject,
+  hasOwn,
+  camelize,
+  hyphenate
+} from './lang'
+import { warn } from './debug'
+import { commonTagRE, reservedTagRE } from './component'
+
+/**
+ * Option overwriting strategies are functions that handle
+ * how to merge a parent option value and a child option
+ * value into the final value.
+ *
+ * All strategy functions follow the same signature:
+ *
+ * @param {*} parentVal
+ * @param {*} childVal
+ * @param {Vue} [vm]
+ */
+
+var strats = config.optionMergeStrategies = Object.create(null)
+
+/**
+ * Helper that recursively merges two data objects together.
+ */
+
+function mergeData (to, from) {
+  var key, toVal, fromVal
+  for (key in from) {
+    toVal = to[key]
+    fromVal = from[key]
+    if (!hasOwn(to, key)) {
+      set(to, key, fromVal)
+    } else if (isObject(toVal) && isObject(fromVal)) {
+      mergeData(toVal, fromVal)
+    }
+  }
+  return to
+}
+
+/**
+ * Data
+ */
+
+strats.data = function (parentVal, childVal, vm) {
+  if (!vm) {
+    // in a Vue.extend merge, both should be functions
+    if (!childVal) {
+      return parentVal
+    }
+    if (typeof childVal !== 'function') {
+      process.env.NODE_ENV !== 'production' && warn(
+        'The "data" option should be a function ' +
+        'that returns a per-instance value in component ' +
+        'definitions.',
+        vm
+      )
+      return parentVal
+    }
+    if (!parentVal) {
+      return childVal
+    }
+    // when parentVal & childVal are both present,
+    // we need to return a function that returns the
+    // merged result of both functions... no need to
+    // check if parentVal is a function here because
+    // it has to be a function to pass previous merges.
+    return function mergedDataFn () {
+      return mergeData(
+        childVal.call(this),
+        parentVal.call(this)
+      )
+    }
+  } else if (parentVal || childVal) {
+    return function mergedInstanceDataFn () {
+      // instance merge
+      var instanceData = typeof childVal === 'function'
+        ? childVal.call(vm)
+        : childVal
+      var defaultData = typeof parentVal === 'function'
+        ? parentVal.call(vm)
+        : undefined
+      if (instanceData) {
+        return mergeData(instanceData, defaultData)
+      } else {
+        return defaultData
+      }
+    }
+  }
+}
+
+/**
+ * El
+ */
+
+strats.el = function (parentVal, childVal, vm) {
+  if (!vm && childVal && typeof childVal !== 'function') {
+    process.env.NODE_ENV !== 'production' && warn(
+      'The "el" option should be a function ' +
+      'that returns a per-instance value in component ' +
+      'definitions.',
+      vm
+    )
+    return
+  }
+  var ret = childVal || parentVal
+  // invoke the element factory if this is instance merge
+  return vm && typeof ret === 'function'
+    ? ret.call(vm)
+    : ret
+}
+
+/**
+ * Hooks and param attributes are merged as arrays.
+ */
+
+strats.init =
+strats.created =
+strats.ready =
+strats.attached =
+strats.detached =
+strats.beforeCompile =
+strats.compiled =
+strats.beforeDestroy =
+strats.destroyed =
+strats.activate = function (parentVal, childVal) {
+  return childVal
+    ? parentVal
+      ? parentVal.concat(childVal)
+      : isArray(childVal)
+        ? childVal
+        : [childVal]
+    : parentVal
+}
+
+/**
+ * Assets
+ *
+ * When a vm is present (instance creation), we need to do
+ * a three-way merge between constructor options, instance
+ * options and parent options.
+ */
+
+function mergeAssets (parentVal, childVal) {
+  var res = Object.create(parentVal || null)
+  return childVal
+    ? extend(res, guardArrayAssets(childVal))
+    : res
+}
+
+config._assetTypes.forEach(function (type) {
+  strats[type + 's'] = mergeAssets
+})
+
+/**
+ * Events & Watchers.
+ *
+ * Events & watchers hashes should not overwrite one
+ * another, so we merge them as arrays.
+ */
+
+strats.watch =
+strats.events = function (parentVal, childVal) {
+  if (!childVal) return parentVal
+  if (!parentVal) return childVal
+  var ret = {}
+  extend(ret, parentVal)
+  for (var key in childVal) {
+    var parent = ret[key]
+    var child = childVal[key]
+    if (parent && !isArray(parent)) {
+      parent = [parent]
+    }
+    ret[key] = parent
+      ? parent.concat(child)
+      : [child]
+  }
+  return ret
+}
+
+/**
+ * Other object hashes.
+ */
+
+strats.props =
+strats.methods =
+strats.computed = function (parentVal, childVal) {
+  if (!childVal) return parentVal
+  if (!parentVal) return childVal
+  var ret = Object.create(null)
+  extend(ret, parentVal)
+  extend(ret, childVal)
+  return ret
+}
+
+/**
+ * Default strategy.
+ */
+
+var defaultStrat = function (parentVal, childVal) {
+  return childVal === undefined
+    ? parentVal
+    : childVal
+}
+
+/**
+ * Make sure component options get converted to actual
+ * constructors.
+ *
+ * @param {Object} options
+ */
+
+function guardComponents (options) {
+  if (options.components) {
+    var components = options.components =
+      guardArrayAssets(options.components)
+    var ids = Object.keys(components)
+    var def
+    if (process.env.NODE_ENV !== 'production') {
+      var map = options._componentNameMap = {}
+    }
+    for (var i = 0, l = ids.length; i < l; i++) {
+      var key = ids[i]
+      if (commonTagRE.test(key) || reservedTagRE.test(key)) {
+        process.env.NODE_ENV !== 'production' && warn(
+          'Do not use built-in or reserved HTML elements as component ' +
+          'id: ' + key
+        )
+        continue
+      }
+      // record a all lowercase <-> kebab-case mapping for
+      // possible custom element case error warning
+      if (process.env.NODE_ENV !== 'production') {
+        map[key.replace(/-/g, '').toLowerCase()] = hyphenate(key)
+      }
+      def = components[key]
+      if (isPlainObject(def)) {
+        components[key] = Vue.extend(def)
+      }
+    }
+  }
+}
+
+/**
+ * Ensure all props option syntax are normalized into the
+ * Object-based format.
+ *
+ * @param {Object} options
+ */
+
+function guardProps (options) {
+  var props = options.props
+  var i, val
+  if (isArray(props)) {
+    options.props = {}
+    i = props.length
+    while (i--) {
+      val = props[i]
+      if (typeof val === 'string') {
+        options.props[val] = null
+      } else if (val.name) {
+        options.props[val.name] = val
+      }
+    }
+  } else if (isPlainObject(props)) {
+    var keys = Object.keys(props)
+    i = keys.length
+    while (i--) {
+      val = props[keys[i]]
+      if (typeof val === 'function') {
+        props[keys[i]] = { type: val }
+      }
+    }
+  }
+}
+
+/**
+ * Guard an Array-format assets option and converted it
+ * into the key-value Object format.
+ *
+ * @param {Object|Array} assets
+ * @return {Object}
+ */
+
+function guardArrayAssets (assets) {
+  if (isArray(assets)) {
+    var res = {}
+    var i = assets.length
+    var asset
+    while (i--) {
+      asset = assets[i]
+      var id = typeof asset === 'function'
+        ? ((asset.options && asset.options.name) || asset.id)
+        : (asset.name || asset.id)
+      if (!id) {
+        process.env.NODE_ENV !== 'production' && warn(
+          'Array-syntax assets must provide a "name" or "id" field.'
+        )
+      } else {
+        res[id] = asset
+      }
+    }
+    return res
+  }
+  return assets
+}
+
+/**
+ * Merge two option objects into a new one.
+ * Core utility used in both instantiation and inheritance.
+ *
+ * @param {Object} parent
+ * @param {Object} child
+ * @param {Vue} [vm] - if vm is present, indicates this is
+ *                     an instantiation merge.
+ */
+
+export function mergeOptions (parent, child, vm) {
+  guardComponents(child)
+  guardProps(child)
+  if (process.env.NODE_ENV !== 'production') {
+    if (child.propsData && !vm) {
+      warn('propsData can only be used as an instantiation option.')
+    }
+  }
+  var options = {}
+  var key
+  if (child.extends) {
+    parent = typeof child.extends === 'function'
+      ? mergeOptions(parent, child.extends.options, vm)
+      : mergeOptions(parent, child.extends, vm)
+  }
+  if (child.mixins) {
+    for (var i = 0, l = child.mixins.length; i < l; i++) {
+      var mixin = child.mixins[i]
+      var mixinOptions = mixin.prototype instanceof Vue
+        ? mixin.options
+        : mixin
+      parent = mergeOptions(parent, mixinOptions, vm)
+    }
+  }
+  for (key in parent) {
+    mergeField(key)
+  }
+  for (key in child) {
+    if (!hasOwn(parent, key)) {
+      mergeField(key)
+    }
+  }
+  function mergeField (key) {
+    var strat = strats[key] || defaultStrat
+    options[key] = strat(parent[key], child[key], vm, key)
+  }
+  return options
+}
+
+/**
+ * Resolve an asset.
+ * This function is used because child instances need access
+ * to assets defined in its ancestor chain.
+ *
+ * @param {Object} options
+ * @param {String} type
+ * @param {String} id
+ * @param {Boolean} warnMissing
+ * @return {Object|Function}
+ */
+
+export function resolveAsset (options, type, id, warnMissing) {
+  /* istanbul ignore if */
+  if (typeof id !== 'string') {
+    return
+  }
+  var assets = options[type]
+  var camelizedId
+  var res = assets[id] ||
+    // camelCase ID
+    assets[camelizedId = camelize(id)] ||
+    // Pascal Case ID
+    assets[camelizedId.charAt(0).toUpperCase() + camelizedId.slice(1)]
+  if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
+    warn(
+      'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
+      options
+    )
+  }
+  return res
+}
diff --git a/src/v3/apiAsyncComponent.ts b/src/v3/apiAsyncComponent.ts
deleted file mode 100644
index 888a0681cb6..00000000000
--- a/src/v3/apiAsyncComponent.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import { warn, isFunction, isObject } from 'core/util'
-
-interface AsyncComponentOptions {
-  loader: Function
-  loadingComponent?: any
-  errorComponent?: any
-  delay?: number
-  timeout?: number
-  suspensible?: boolean
-  onError?: (
-    error: Error,
-    retry: () => void,
-    fail: () => void,
-    attempts: number
-  ) => any
-}
-
-type AsyncComponentFactory = () => {
-  component: Promise<any>
-  loading?: any
-  error?: any
-  delay?: number
-  timeout?: number
-}
-
-/**
- * v3-compatible async component API.
- * @internal the type is manually declared in <root>/types/v3-define-async-component.d.ts
- * because it relies on existing manual types
- */
-export function defineAsyncComponent(
-  source: (() => any) | AsyncComponentOptions
-): AsyncComponentFactory {
-  if (isFunction(source)) {
-    source = { loader: source } as AsyncComponentOptions
-  }
-
-  const {
-    loader,
-    loadingComponent,
-    errorComponent,
-    delay = 200,
-    timeout, // undefined = never times out
-    suspensible = false, // in Vue 3 default is true
-    onError: userOnError
-  } = source
-
-  if (__DEV__ && suspensible) {
-    warn(
-      `The suspensible option for async components is not supported in Vue2. It is ignored.`
-    )
-  }
-
-  let pendingRequest: Promise<any> | null = null
-
-  let retries = 0
-  const retry = () => {
-    retries++
-    pendingRequest = null
-    return load()
-  }
-
-  const load = (): Promise<any> => {
-    let thisRequest: Promise<any>
-    return (
-      pendingRequest ||
-      (thisRequest = pendingRequest =
-        loader()
-          .catch(err => {
-            err = err instanceof Error ? err : new Error(String(err))
-            if (userOnError) {
-              return new Promise((resolve, reject) => {
-                const userRetry = () => resolve(retry())
-                const userFail = () => reject(err)
-                userOnError(err, userRetry, userFail, retries + 1)
-              })
-            } else {
-              throw err
-            }
-          })
-          .then((comp: any) => {
-            if (thisRequest !== pendingRequest && pendingRequest) {
-              return pendingRequest
-            }
-            if (__DEV__ && !comp) {
-              warn(
-                `Async component loader resolved to undefined. ` +
-                  `If you are using retry(), make sure to return its return value.`
-              )
-            }
-            // interop module default
-            if (
-              comp &&
-              (comp.__esModule || comp[Symbol.toStringTag] === 'Module')
-            ) {
-              comp = comp.default
-            }
-            if (__DEV__ && comp && !isObject(comp) && !isFunction(comp)) {
-              throw new Error(`Invalid async component load result: ${comp}`)
-            }
-            return comp
-          }))
-    )
-  }
-
-  return () => {
-    const component = load()
-
-    return {
-      component,
-      delay,
-      timeout,
-      error: errorComponent,
-      loading: loadingComponent
-    }
-  }
-}
diff --git a/src/v3/apiInject.ts b/src/v3/apiInject.ts
deleted file mode 100644
index 76928268b13..00000000000
--- a/src/v3/apiInject.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import { isFunction, warn } from 'core/util'
-import { currentInstance } from './currentInstance'
-import type { Component } from 'types/component'
-
-export interface InjectionKey<T> extends Symbol {}
-
-export function provide<T>(key: InjectionKey<T> | string | number, value: T) {
-  if (!currentInstance) {
-    if (__DEV__) {
-      warn(`provide() can only be used inside setup().`)
-    }
-  } else {
-    // TS doesn't allow symbol as index type
-    resolveProvided(currentInstance)[key as string] = value
-  }
-}
-
-export function resolveProvided(vm: Component): Record<string, any> {
-  // by default an instance inherits its parent's provides object
-  // but when it needs to provide values of its own, it creates its
-  // own provides object using parent provides object as prototype.
-  // this way in `inject` we can simply look up injections from direct
-  // parent and let the prototype chain do the work.
-  const existing = vm._provided
-  const parentProvides = vm.$parent && vm.$parent._provided
-  if (parentProvides === existing) {
-    return (vm._provided = Object.create(parentProvides))
-  } else {
-    return existing
-  }
-}
-
-export function inject<T>(key: InjectionKey<T> | string): T | undefined
-export function inject<T>(
-  key: InjectionKey<T> | string,
-  defaultValue: T,
-  treatDefaultAsFactory?: false
-): T
-export function inject<T>(
-  key: InjectionKey<T> | string,
-  defaultValue: T | (() => T),
-  treatDefaultAsFactory: true
-): T
-export function inject(
-  key: InjectionKey<any> | string,
-  defaultValue?: unknown,
-  treatDefaultAsFactory = false
-) {
-  // fallback to `currentRenderingInstance` so that this can be called in
-  // a functional component
-  const instance = currentInstance
-  if (instance) {
-    // #2400
-    // to support `app.use` plugins,
-    // fallback to appContext's `provides` if the instance is at root
-    const provides = instance.$parent && instance.$parent._provided
-
-    if (provides && (key as string | symbol) in provides) {
-      // TS doesn't allow symbol as index type
-      return provides[key as string]
-    } else if (arguments.length > 1) {
-      return treatDefaultAsFactory && isFunction(defaultValue)
-        ? defaultValue.call(instance)
-        : defaultValue
-    } else if (__DEV__) {
-      warn(`injection "${String(key)}" not found.`)
-    }
-  } else if (__DEV__) {
-    warn(`inject() can only be used inside setup() or functional components.`)
-  }
-}
diff --git a/src/v3/apiLifecycle.ts b/src/v3/apiLifecycle.ts
deleted file mode 100644
index 31e0542920c..00000000000
--- a/src/v3/apiLifecycle.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import { DebuggerEvent } from './debug'
-import { Component } from 'types/component'
-import { mergeLifecycleHook, warn } from '../core/util'
-import { currentInstance } from './currentInstance'
-
-function createLifeCycle<T extends (...args: any[]) => any = () => void>(
-  hookName: string
-) {
-  return (fn: T, target: any = currentInstance) => {
-    if (!target) {
-      __DEV__ &&
-        warn(
-          `${formatName(
-            hookName
-          )} is called when there is no active component instance to be ` +
-            `associated with. ` +
-            `Lifecycle injection APIs can only be used during execution of setup().`
-        )
-      return
-    }
-    return injectHook(target, hookName, fn)
-  }
-}
-
-function formatName(name: string) {
-  if (name === 'beforeDestroy') {
-    name = 'beforeUnmount'
-  } else if (name === 'destroyed') {
-    name = 'unmounted'
-  }
-  return `on${name[0].toUpperCase() + name.slice(1)}`
-}
-
-function injectHook(instance: Component, hookName: string, fn: () => void) {
-  const options = instance.$options
-  options[hookName] = mergeLifecycleHook(options[hookName], fn)
-}
-
-export const onBeforeMount = createLifeCycle('beforeMount')
-export const onMounted = createLifeCycle('mounted')
-export const onBeforeUpdate = createLifeCycle('beforeUpdate')
-export const onUpdated = createLifeCycle('updated')
-export const onBeforeUnmount = createLifeCycle('beforeDestroy')
-export const onUnmounted = createLifeCycle('destroyed')
-export const onActivated = createLifeCycle('activated')
-export const onDeactivated = createLifeCycle('deactivated')
-export const onServerPrefetch = createLifeCycle('serverPrefetch')
-
-export const onRenderTracked =
-  createLifeCycle<(e: DebuggerEvent) => any>('renderTracked')
-export const onRenderTriggered =
-  createLifeCycle<(e: DebuggerEvent) => any>('renderTriggered')
-
-export type ErrorCapturedHook<TError = unknown> = (
-  err: TError,
-  instance: any,
-  info: string
-) => boolean | void
-
-const injectErrorCapturedHook =
-  createLifeCycle<ErrorCapturedHook<any>>('errorCaptured')
-
-export function onErrorCaptured<TError = Error>(
-  hook: ErrorCapturedHook<TError>,
-  target: any = currentInstance
-) {
-  injectErrorCapturedHook(hook, target)
-}
diff --git a/src/v3/apiSetup.ts b/src/v3/apiSetup.ts
deleted file mode 100644
index 064e15dc75f..00000000000
--- a/src/v3/apiSetup.ts
+++ /dev/null
@@ -1,246 +0,0 @@
-import { Component } from 'types/component'
-import { PropOptions } from 'types/options'
-import { popTarget, pushTarget } from '../core/observer/dep'
-import { def, invokeWithErrorHandling, isReserved, warn } from '../core/util'
-import VNode from '../core/vdom/vnode'
-import {
-  bind,
-  emptyObject,
-  isArray,
-  isFunction,
-  isObject
-} from '../shared/util'
-import { currentInstance, setCurrentInstance } from './currentInstance'
-import { shallowReactive } from './reactivity/reactive'
-import { proxyWithRefUnwrap } from './reactivity/ref'
-
-/**
- * @internal
- */
-export interface SetupContext {
-  attrs: Record<string, any>
-  listeners: Record<string, Function | Function[]>
-  slots: Record<string, () => VNode[]>
-  emit: (event: string, ...args: any[]) => any
-  expose: (exposed: Record<string, any>) => void
-}
-
-export function initSetup(vm: Component) {
-  const options = vm.$options
-  const setup = options.setup
-  if (setup) {
-    const ctx = (vm._setupContext = createSetupContext(vm))
-
-    setCurrentInstance(vm)
-    pushTarget()
-    const setupResult = invokeWithErrorHandling(
-      setup,
-      null,
-      [vm._props || shallowReactive({}), ctx],
-      vm,
-      `setup`
-    )
-    popTarget()
-    setCurrentInstance()
-
-    if (isFunction(setupResult)) {
-      // render function
-      // @ts-ignore
-      options.render = setupResult
-    } else if (isObject(setupResult)) {
-      // bindings
-      if (__DEV__ && setupResult instanceof VNode) {
-        warn(
-          `setup() should not return VNodes directly - ` +
-            `return a render function instead.`
-        )
-      }
-      vm._setupState = setupResult
-      // __sfc indicates compiled bindings from <script setup>
-      if (!setupResult.__sfc) {
-        for (const key in setupResult) {
-          if (!isReserved(key)) {
-            proxyWithRefUnwrap(vm, setupResult, key)
-          } else if (__DEV__) {
-            warn(`Avoid using variables that start with _ or $ in setup().`)
-          }
-        }
-      } else {
-        // exposed for compiled render fn
-        const proxy = (vm._setupProxy = {})
-        for (const key in setupResult) {
-          if (key !== '__sfc') {
-            proxyWithRefUnwrap(proxy, setupResult, key)
-          }
-        }
-      }
-    } else if (__DEV__ && setupResult !== undefined) {
-      warn(
-        `setup() should return an object. Received: ${
-          setupResult === null ? 'null' : typeof setupResult
-        }`
-      )
-    }
-  }
-}
-
-function createSetupContext(vm: Component): SetupContext {
-  let exposeCalled = false
-  return {
-    get attrs() {
-      if (!vm._attrsProxy) {
-        const proxy = (vm._attrsProxy = {})
-        def(proxy, '_v_attr_proxy', true)
-        syncSetupProxy(proxy, vm.$attrs, emptyObject, vm, '$attrs')
-      }
-      return vm._attrsProxy
-    },
-    get listeners() {
-      if (!vm._listenersProxy) {
-        const proxy = (vm._listenersProxy = {})
-        syncSetupProxy(proxy, vm.$listeners, emptyObject, vm, '$listeners')
-      }
-      return vm._listenersProxy
-    },
-    get slots() {
-      return initSlotsProxy(vm)
-    },
-    emit: bind(vm.$emit, vm) as any,
-    expose(exposed?: Record<string, any>) {
-      if (__DEV__) {
-        if (exposeCalled) {
-          warn(`expose() should be called only once per setup().`, vm)
-        }
-        exposeCalled = true
-      }
-      if (exposed) {
-        Object.keys(exposed).forEach(key =>
-          proxyWithRefUnwrap(vm, exposed, key)
-        )
-      }
-    }
-  }
-}
-
-export function syncSetupProxy(
-  to: any,
-  from: any,
-  prev: any,
-  instance: Component,
-  type: string
-) {
-  let changed = false
-  for (const key in from) {
-    if (!(key in to)) {
-      changed = true
-      defineProxyAttr(to, key, instance, type)
-    } else if (from[key] !== prev[key]) {
-      changed = true
-    }
-  }
-  for (const key in to) {
-    if (!(key in from)) {
-      changed = true
-      delete to[key]
-    }
-  }
-  return changed
-}
-
-function defineProxyAttr(
-  proxy: any,
-  key: string,
-  instance: Component,
-  type: string
-) {
-  Object.defineProperty(proxy, key, {
-    enumerable: true,
-    configurable: true,
-    get() {
-      return instance[type][key]
-    }
-  })
-}
-
-function initSlotsProxy(vm: Component) {
-  if (!vm._slotsProxy) {
-    syncSetupSlots((vm._slotsProxy = {}), vm.$scopedSlots)
-  }
-  return vm._slotsProxy
-}
-
-export function syncSetupSlots(to: any, from: any) {
-  for (const key in from) {
-    to[key] = from[key]
-  }
-  for (const key in to) {
-    if (!(key in from)) {
-      delete to[key]
-    }
-  }
-}
-
-/**
- * @internal use manual type def because public setup context type relies on
- * legacy VNode types
- */
-export function useSlots(): SetupContext['slots'] {
-  return getContext().slots
-}
-
-/**
- * @internal use manual type def because public setup context type relies on
- * legacy VNode types
- */
-export function useAttrs(): SetupContext['attrs'] {
-  return getContext().attrs
-}
-
-/**
- * Vue 2 only
- * @internal use manual type def because public setup context type relies on
- * legacy VNode types
- */
-export function useListeners(): SetupContext['listeners'] {
-  return getContext().listeners
-}
-
-function getContext(): SetupContext {
-  if (__DEV__ && !currentInstance) {
-    warn(`useContext() called without active instance.`)
-  }
-  const vm = currentInstance!
-  return vm._setupContext || (vm._setupContext = createSetupContext(vm))
-}
-
-/**
- * Runtime helper for merging default declarations. Imported by compiled code
- * only.
- * @internal
- */
-export function mergeDefaults(
-  raw: string[] | Record<string, PropOptions>,
-  defaults: Record<string, any>
-): Record<string, PropOptions> {
-  const props = isArray(raw)
-    ? raw.reduce(
-        (normalized, p) => ((normalized[p] = {}), normalized),
-        {} as Record<string, PropOptions>
-      )
-    : raw
-  for (const key in defaults) {
-    const opt = props[key]
-    if (opt) {
-      if (isArray(opt) || isFunction(opt)) {
-        props[key] = { type: opt, default: defaults[key] }
-      } else {
-        opt.default = defaults[key]
-      }
-    } else if (opt === null) {
-      props[key] = { default: defaults[key] }
-    } else if (__DEV__) {
-      warn(`props default key "${key}" has no corresponding declaration.`)
-    }
-  }
-  return props
-}
diff --git a/src/v3/apiWatch.ts b/src/v3/apiWatch.ts
deleted file mode 100644
index 7160009dabb..00000000000
--- a/src/v3/apiWatch.ts
+++ /dev/null
@@ -1,353 +0,0 @@
-import { isRef, Ref } from './reactivity/ref'
-import { ComputedRef } from './reactivity/computed'
-import { isReactive, isShallow } from './reactivity/reactive'
-import {
-  warn,
-  noop,
-  isArray,
-  isFunction,
-  emptyObject,
-  hasChanged,
-  isServerRendering,
-  invokeWithErrorHandling
-} from 'core/util'
-import { currentInstance } from './currentInstance'
-import { traverse } from 'core/observer/traverse'
-import Watcher from '../core/observer/watcher'
-import { queueWatcher } from '../core/observer/scheduler'
-import { DebuggerOptions } from './debug'
-
-const WATCHER = `watcher`
-const WATCHER_CB = `${WATCHER} callback`
-const WATCHER_GETTER = `${WATCHER} getter`
-const WATCHER_CLEANUP = `${WATCHER} cleanup`
-
-export type WatchEffect = (onCleanup: OnCleanup) => void
-
-export type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
-
-export type WatchCallback<V = any, OV = any> = (
-  value: V,
-  oldValue: OV,
-  onCleanup: OnCleanup
-) => any
-
-type MapSources<T, Immediate> = {
-  [K in keyof T]: T[K] extends WatchSource<infer V>
-    ? Immediate extends true
-      ? V | undefined
-      : V
-    : T[K] extends object
-    ? Immediate extends true
-      ? T[K] | undefined
-      : T[K]
-    : never
-}
-
-type OnCleanup = (cleanupFn: () => void) => void
-
-export interface WatchOptionsBase extends DebuggerOptions {
-  flush?: 'pre' | 'post' | 'sync'
-}
-
-export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
-  immediate?: Immediate
-  deep?: boolean
-}
-
-export type WatchStopHandle = () => void
-
-// Simple effect.
-export function watchEffect(
-  effect: WatchEffect,
-  options?: WatchOptionsBase
-): WatchStopHandle {
-  return doWatch(effect, null, options)
-}
-
-export function watchPostEffect(
-  effect: WatchEffect,
-  options?: DebuggerOptions
-) {
-  return doWatch(
-    effect,
-    null,
-    (__DEV__
-      ? { ...options, flush: 'post' }
-      : { flush: 'post' }) as WatchOptionsBase
-  )
-}
-
-export function watchSyncEffect(
-  effect: WatchEffect,
-  options?: DebuggerOptions
-) {
-  return doWatch(
-    effect,
-    null,
-    (__DEV__
-      ? { ...options, flush: 'sync' }
-      : { flush: 'sync' }) as WatchOptionsBase
-  )
-}
-
-// initial value for watchers to trigger on undefined initial values
-const INITIAL_WATCHER_VALUE = {}
-
-type MultiWatchSources = (WatchSource<unknown> | object)[]
-
-// overload: array of multiple sources + cb
-export function watch<
-  T extends MultiWatchSources,
-  Immediate extends Readonly<boolean> = false
->(
-  sources: [...T],
-  cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
-  options?: WatchOptions<Immediate>
-): WatchStopHandle
-
-// overload: multiple sources w/ `as const`
-// watch([foo, bar] as const, () => {})
-// somehow [...T] breaks when the type is readonly
-export function watch<
-  T extends Readonly<MultiWatchSources>,
-  Immediate extends Readonly<boolean> = false
->(
-  source: T,
-  cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
-  options?: WatchOptions<Immediate>
-): WatchStopHandle
-
-// overload: single source + cb
-export function watch<T, Immediate extends Readonly<boolean> = false>(
-  source: WatchSource<T>,
-  cb: WatchCallback<T, Immediate extends true ? T | undefined : T>,
-  options?: WatchOptions<Immediate>
-): WatchStopHandle
-
-// overload: watching reactive object w/ cb
-export function watch<
-  T extends object,
-  Immediate extends Readonly<boolean> = false
->(
-  source: T,
-  cb: WatchCallback<T, Immediate extends true ? T | undefined : T>,
-  options?: WatchOptions<Immediate>
-): WatchStopHandle
-
-// implementation
-export function watch<T = any, Immediate extends Readonly<boolean> = false>(
-  source: T | WatchSource<T>,
-  cb: any,
-  options?: WatchOptions<Immediate>
-): WatchStopHandle {
-  if (__DEV__ && typeof cb !== 'function') {
-    warn(
-      `\`watch(fn, options?)\` signature has been moved to a separate API. ` +
-        `Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
-        `supports \`watch(source, cb, options?) signature.`
-    )
-  }
-  return doWatch(source as any, cb, options)
-}
-
-function doWatch(
-  source: WatchSource | WatchSource[] | WatchEffect | object,
-  cb: WatchCallback | null,
-  {
-    immediate,
-    deep,
-    flush = 'pre',
-    onTrack,
-    onTrigger
-  }: WatchOptions = emptyObject
-): WatchStopHandle {
-  if (__DEV__ && !cb) {
-    if (immediate !== undefined) {
-      warn(
-        `watch() "immediate" option is only respected when using the ` +
-          `watch(source, callback, options?) signature.`
-      )
-    }
-    if (deep !== undefined) {
-      warn(
-        `watch() "deep" option is only respected when using the ` +
-          `watch(source, callback, options?) signature.`
-      )
-    }
-  }
-
-  const warnInvalidSource = (s: unknown) => {
-    warn(
-      `Invalid watch source: ${s}. A watch source can only be a getter/effect ` +
-        `function, a ref, a reactive object, or an array of these types.`
-    )
-  }
-
-  const instance = currentInstance
-  const call = (fn: Function, type: string, args: any[] | null = null) => {
-    const res = invokeWithErrorHandling(fn, null, args, instance, type)
-    if (deep && res && res.__ob__) res.__ob__.dep.depend()
-    return res
-  }
-
-  let getter: () => any
-  let forceTrigger = false
-  let isMultiSource = false
-
-  if (isRef(source)) {
-    getter = () => source.value
-    forceTrigger = isShallow(source)
-  } else if (isReactive(source)) {
-    getter = () => {
-      ;(source as any).__ob__.dep.depend()
-      return source
-    }
-    deep = true
-  } else if (isArray(source)) {
-    isMultiSource = true
-    forceTrigger = source.some(s => isReactive(s) || isShallow(s))
-    getter = () =>
-      source.map(s => {
-        if (isRef(s)) {
-          return s.value
-        } else if (isReactive(s)) {
-          s.__ob__.dep.depend()
-          return traverse(s)
-        } else if (isFunction(s)) {
-          return call(s, WATCHER_GETTER)
-        } else {
-          __DEV__ && warnInvalidSource(s)
-        }
-      })
-  } else if (isFunction(source)) {
-    if (cb) {
-      // getter with cb
-      getter = () => call(source, WATCHER_GETTER)
-    } else {
-      // no cb -> simple effect
-      getter = () => {
-        if (instance && instance._isDestroyed) {
-          return
-        }
-        if (cleanup) {
-          cleanup()
-        }
-        return call(source, WATCHER, [onCleanup])
-      }
-    }
-  } else {
-    getter = noop
-    __DEV__ && warnInvalidSource(source)
-  }
-
-  if (cb && deep) {
-    const baseGetter = getter
-    getter = () => traverse(baseGetter())
-  }
-
-  let cleanup: () => void
-  let onCleanup: OnCleanup = (fn: () => void) => {
-    cleanup = watcher.onStop = () => {
-      call(fn, WATCHER_CLEANUP)
-    }
-  }
-
-  // in SSR there is no need to setup an actual effect, and it should be noop
-  // unless it's eager
-  if (isServerRendering()) {
-    // we will also not call the invalidate callback (+ runner is not set up)
-    onCleanup = noop
-    if (!cb) {
-      getter()
-    } else if (immediate) {
-      call(cb, WATCHER_CB, [
-        getter(),
-        isMultiSource ? [] : undefined,
-        onCleanup
-      ])
-    }
-    return noop
-  }
-
-  const watcher = new Watcher(currentInstance, getter, noop, {
-    lazy: true
-  })
-  watcher.noRecurse = !cb
-
-  let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE
-  // overwrite default run
-  watcher.run = () => {
-    if (!watcher.active) {
-      return
-    }
-    if (cb) {
-      // watch(source, cb)
-      const newValue = watcher.get()
-      if (
-        deep ||
-        forceTrigger ||
-        (isMultiSource
-          ? (newValue as any[]).some((v, i) =>
-              hasChanged(v, (oldValue as any[])[i])
-            )
-          : hasChanged(newValue, oldValue))
-      ) {
-        // cleanup before running cb again
-        if (cleanup) {
-          cleanup()
-        }
-        call(cb, WATCHER_CB, [
-          newValue,
-          // pass undefined as the old value when it's changed for the first time
-          oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
-          onCleanup
-        ])
-        oldValue = newValue
-      }
-    } else {
-      // watchEffect
-      watcher.get()
-    }
-  }
-
-  if (flush === 'sync') {
-    watcher.update = watcher.run
-  } else if (flush === 'post') {
-    watcher.post = true
-    watcher.update = () => queueWatcher(watcher)
-  } else {
-    // pre
-    watcher.update = () => {
-      if (instance && instance === currentInstance && !instance._isMounted) {
-        // pre-watcher triggered before
-        const buffer = instance._preWatchers || (instance._preWatchers = [])
-        if (buffer.indexOf(watcher) < 0) buffer.push(watcher)
-      } else {
-        queueWatcher(watcher)
-      }
-    }
-  }
-
-  if (__DEV__) {
-    watcher.onTrack = onTrack
-    watcher.onTrigger = onTrigger
-  }
-
-  // initial run
-  if (cb) {
-    if (immediate) {
-      watcher.run()
-    } else {
-      oldValue = watcher.get()
-    }
-  } else if (flush === 'post' && instance) {
-    instance.$once('hook:mounted', () => watcher.get())
-  } else {
-    watcher.get()
-  }
-
-  return () => {
-    watcher.teardown()
-  }
-}
diff --git a/src/v3/currentInstance.ts b/src/v3/currentInstance.ts
deleted file mode 100644
index 197f9392945..00000000000
--- a/src/v3/currentInstance.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Component } from 'types/component'
-
-export let currentInstance: Component | null = null
-
-/**
- * This is exposed for compatibility with v3 (e.g. some functions in VueUse
- * relies on it). Do not use this internally, just use `currentInstance`.
- *
- * @internal this function needs manual type declaration because it relies
- * on previously manually authored types from Vue 2
- */
-export function getCurrentInstance(): { proxy: Component } | null {
-  return currentInstance && { proxy: currentInstance }
-}
-
-/**
- * @internal
- */
-export function setCurrentInstance(vm: Component | null = null) {
-  if (!vm) currentInstance && currentInstance._scope.off()
-  currentInstance = vm
-  vm && vm._scope.on()
-}
diff --git a/src/v3/debug.ts b/src/v3/debug.ts
deleted file mode 100644
index ff6890b9c0c..00000000000
--- a/src/v3/debug.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { TrackOpTypes, TriggerOpTypes } from './reactivity/operations'
-
-export interface DebuggerOptions {
-  onTrack?: (event: DebuggerEvent) => void
-  onTrigger?: (event: DebuggerEvent) => void
-}
-
-export type DebuggerEvent = {
-  /**
-   * @internal
-   */
-  effect: any
-} & DebuggerEventExtraInfo
-
-export type DebuggerEventExtraInfo = {
-  target: object
-  type: TrackOpTypes | TriggerOpTypes
-  key?: any
-  newValue?: any
-  oldValue?: any
-}
diff --git a/src/v3/h.ts b/src/v3/h.ts
deleted file mode 100644
index 292446cad3d..00000000000
--- a/src/v3/h.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { createElement } from '../core/vdom/create-element'
-import { currentInstance } from './currentInstance'
-import { warn } from 'core/util'
-
-/**
- * @internal this function needs manual public type declaration because it relies
- * on previously manually authored types from Vue 2
- */
-export function h(type: any, props?: any, children?: any) {
-  if (!currentInstance) {
-    __DEV__ &&
-      warn(
-        `globally imported h() can only be invoked when there is an active ` +
-          `component instance, e.g. synchronously in a component's render or setup function.`
-      )
-  }
-  return createElement(currentInstance!, type, props, children, 2, true)
-}
diff --git a/src/v3/index.ts b/src/v3/index.ts
deleted file mode 100644
index 90dfacc17ed..00000000000
--- a/src/v3/index.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * Note: also update dist/vue.runtime.mjs when adding new exports to this file.
- */
-
-export const version: string = '__VERSION__'
-
-export {
-  ref,
-  shallowRef,
-  isRef,
-  toRef,
-  toRefs,
-  unref,
-  proxyRefs,
-  customRef,
-  triggerRef,
-  Ref,
-  ToRef,
-  ToRefs,
-  UnwrapRef,
-  ShallowRef,
-  ShallowUnwrapRef,
-  RefUnwrapBailTypes,
-  CustomRefFactory
-} from './reactivity/ref'
-
-export {
-  reactive,
-  isReactive,
-  isReadonly,
-  isShallow,
-  isProxy,
-  shallowReactive,
-  markRaw,
-  toRaw,
-  ReactiveFlags,
-  ShallowReactive,
-  UnwrapNestedRefs
-} from './reactivity/reactive'
-
-export { readonly, shallowReadonly, DeepReadonly } from './reactivity/readonly'
-
-export {
-  computed,
-  ComputedRef,
-  WritableComputedRef,
-  WritableComputedOptions,
-  ComputedGetter,
-  ComputedSetter
-} from './reactivity/computed'
-
-export {
-  watch,
-  watchEffect,
-  watchPostEffect,
-  watchSyncEffect,
-  WatchEffect,
-  WatchOptions,
-  WatchOptionsBase,
-  WatchCallback,
-  WatchSource,
-  WatchStopHandle
-} from './apiWatch'
-
-export {
-  EffectScope,
-  effectScope,
-  onScopeDispose,
-  getCurrentScope
-} from './reactivity/effectScope'
-
-export { DebuggerOptions, DebuggerEvent, DebuggerEventExtraInfo } from './debug'
-
-export { TrackOpTypes, TriggerOpTypes } from './reactivity/operations'
-
-export { provide, inject, InjectionKey } from './apiInject'
-
-export { h } from './h'
-export { getCurrentInstance } from './currentInstance'
-export { useSlots, useAttrs, useListeners, mergeDefaults } from './apiSetup'
-export { nextTick } from 'core/util/next-tick'
-export { set, del } from 'core/observer'
-
-export { useCssModule } from './sfc-helpers/useCssModule'
-export { useCssVars } from './sfc-helpers/useCssVars'
-
-/**
- * @internal type is manually declared in <root>/types/v3-define-component.d.ts
- */
-export function defineComponent(options: any) {
-  return options
-}
-
-export { defineAsyncComponent } from './apiAsyncComponent'
-
-export * from './apiLifecycle'
diff --git a/src/v3/reactivity/computed.ts b/src/v3/reactivity/computed.ts
deleted file mode 100644
index 7a38c3cfc0a..00000000000
--- a/src/v3/reactivity/computed.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { isServerRendering, noop, warn, def, isFunction } from 'core/util'
-import { Ref, RefFlag } from './ref'
-import Watcher from 'core/observer/watcher'
-import Dep from 'core/observer/dep'
-import { currentInstance } from '../currentInstance'
-import { ReactiveFlags } from './reactive'
-import { TrackOpTypes } from './operations'
-import { DebuggerOptions } from '../debug'
-
-declare const ComputedRefSymbol: unique symbol
-
-export interface ComputedRef<T = any> extends WritableComputedRef<T> {
-  readonly value: T
-  [ComputedRefSymbol]: true
-}
-
-export interface WritableComputedRef<T> extends Ref<T> {
-  readonly effect: any /* Watcher */
-}
-
-export type ComputedGetter<T> = (...args: any[]) => T
-export type ComputedSetter<T> = (v: T) => void
-
-export interface WritableComputedOptions<T> {
-  get: ComputedGetter<T>
-  set: ComputedSetter<T>
-}
-
-export function computed<T>(
-  getter: ComputedGetter<T>,
-  debugOptions?: DebuggerOptions
-): ComputedRef<T>
-export function computed<T>(
-  options: WritableComputedOptions<T>,
-  debugOptions?: DebuggerOptions
-): WritableComputedRef<T>
-export function computed<T>(
-  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,
-  debugOptions?: DebuggerOptions
-) {
-  let getter: ComputedGetter<T>
-  let setter: ComputedSetter<T>
-
-  const onlyGetter = isFunction(getterOrOptions)
-  if (onlyGetter) {
-    getter = getterOrOptions
-    setter = __DEV__
-      ? () => {
-          warn('Write operation failed: computed value is readonly')
-        }
-      : noop
-  } else {
-    getter = getterOrOptions.get
-    setter = getterOrOptions.set
-  }
-
-  const watcher = isServerRendering()
-    ? null
-    : new Watcher(currentInstance, getter, noop, { lazy: true })
-
-  if (__DEV__ && watcher && debugOptions) {
-    watcher.onTrack = debugOptions.onTrack
-    watcher.onTrigger = debugOptions.onTrigger
-  }
-
-  const ref = {
-    // some libs rely on the presence effect for checking computed refs
-    // from normal refs, but the implementation doesn't matter
-    effect: watcher,
-    get value() {
-      if (watcher) {
-        if (watcher.dirty) {
-          watcher.evaluate()
-        }
-        if (Dep.target) {
-          if (__DEV__ && Dep.target.onTrack) {
-            Dep.target.onTrack({
-              effect: Dep.target,
-              target: ref,
-              type: TrackOpTypes.GET,
-              key: 'value'
-            })
-          }
-          watcher.depend()
-        }
-        return watcher.value
-      } else {
-        return getter()
-      }
-    },
-    set value(newVal) {
-      setter(newVal)
-    }
-  } as any
-
-  def(ref, RefFlag, true)
-  def(ref, ReactiveFlags.IS_READONLY, onlyGetter)
-
-  return ref
-}
diff --git a/src/v3/reactivity/effect.ts b/src/v3/reactivity/effect.ts
deleted file mode 100644
index 11f9534a31b..00000000000
--- a/src/v3/reactivity/effect.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import Watcher from 'core/observer/watcher'
-import { noop } from 'shared/util'
-import { currentInstance } from '../currentInstance'
-
-// export type EffectScheduler = (...args: any[]) => any
-
-/**
- * @internal since we are not exposing this in Vue 2, it's used only for
- * internal testing.
- */
-export function effect(fn: () => any, scheduler?: (cb: any) => void) {
-  const watcher = new Watcher(currentInstance, fn, noop, {
-    sync: true
-  })
-  if (scheduler) {
-    watcher.update = () => {
-      scheduler(() => watcher.run())
-    }
-  }
-}
diff --git a/src/v3/reactivity/effectScope.ts b/src/v3/reactivity/effectScope.ts
deleted file mode 100644
index f60e5fccf23..00000000000
--- a/src/v3/reactivity/effectScope.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import Watcher from 'core/observer/watcher'
-import { warn } from 'core/util'
-
-export let activeEffectScope: EffectScope | undefined
-
-export class EffectScope {
-  /**
-   * @internal
-   */
-  active = true
-  /**
-   * @internal
-   */
-  effects: Watcher[] = []
-  /**
-   * @internal
-   */
-  cleanups: (() => void)[] = []
-  /**
-   * @internal
-   */
-  parent: EffectScope | undefined
-  /**
-   * record undetached scopes
-   * @internal
-   */
-  scopes: EffectScope[] | undefined
-  /**
-   * indicates this being a component root scope
-   * @internal
-   */
-  _vm?: boolean
-  /**
-   * track a child scope's index in its parent's scopes array for optimized
-   * removal
-   */
-  private index: number | undefined
-
-  constructor(public detached = false) {
-    this.parent = activeEffectScope
-    if (!detached && activeEffectScope) {
-      this.index =
-        (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(
-          this
-        ) - 1
-    }
-  }
-
-  run<T>(fn: () => T): T | undefined {
-    if (this.active) {
-      const currentEffectScope = activeEffectScope
-      try {
-        activeEffectScope = this
-        return fn()
-      } finally {
-        activeEffectScope = currentEffectScope
-      }
-    } else if (__DEV__) {
-      warn(`cannot run an inactive effect scope.`)
-    }
-  }
-
-  /**
-   * This should only be called on non-detached scopes
-   * @internal
-   */
-  on() {
-    activeEffectScope = this
-  }
-
-  /**
-   * This should only be called on non-detached scopes
-   * @internal
-   */
-  off() {
-    activeEffectScope = this.parent
-  }
-
-  stop(fromParent?: boolean) {
-    if (this.active) {
-      let i, l
-      for (i = 0, l = this.effects.length; i < l; i++) {
-        this.effects[i].teardown()
-      }
-      for (i = 0, l = this.cleanups.length; i < l; i++) {
-        this.cleanups[i]()
-      }
-      if (this.scopes) {
-        for (i = 0, l = this.scopes.length; i < l; i++) {
-          this.scopes[i].stop(true)
-        }
-      }
-      // nested scope, dereference from parent to avoid memory leaks
-      if (!this.detached && this.parent && !fromParent) {
-        // optimized O(1) removal
-        const last = this.parent.scopes!.pop()
-        if (last && last !== this) {
-          this.parent.scopes![this.index!] = last
-          last.index = this.index!
-        }
-      }
-      this.parent = undefined
-      this.active = false
-    }
-  }
-}
-
-export function effectScope(detached?: boolean) {
-  return new EffectScope(detached)
-}
-
-/**
- * @internal
- */
-export function recordEffectScope(
-  effect: Watcher,
-  scope: EffectScope | undefined = activeEffectScope
-) {
-  if (scope && scope.active) {
-    scope.effects.push(effect)
-  }
-}
-
-export function getCurrentScope() {
-  return activeEffectScope
-}
-
-export function onScopeDispose(fn: () => void) {
-  if (activeEffectScope) {
-    activeEffectScope.cleanups.push(fn)
-  } else if (__DEV__) {
-    warn(
-      `onScopeDispose() is called when there is no active effect scope` +
-        ` to be associated with.`
-    )
-  }
-}
diff --git a/src/v3/reactivity/operations.ts b/src/v3/reactivity/operations.ts
deleted file mode 100644
index 9e6bec51878..00000000000
--- a/src/v3/reactivity/operations.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-// using literal strings instead of numbers so that it's easier to inspect
-// debugger events
-
-export const enum TrackOpTypes {
-  GET = 'get',
-  TOUCH = 'touch'
-}
-
-export const enum TriggerOpTypes {
-  SET = 'set',
-  ADD = 'add',
-  DELETE = 'delete',
-  ARRAY_MUTATION = 'array mutation'
-}
diff --git a/src/v3/reactivity/reactive.ts b/src/v3/reactivity/reactive.ts
deleted file mode 100644
index da9a1bf0c1c..00000000000
--- a/src/v3/reactivity/reactive.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import { observe, Observer } from 'core/observer'
-import {
-  def,
-  isArray,
-  isPrimitive,
-  warn,
-  toRawType,
-  isServerRendering
-} from 'core/util'
-import type { Ref, UnwrapRefSimple, RawSymbol } from './ref'
-
-export const enum ReactiveFlags {
-  SKIP = '__v_skip',
-  IS_READONLY = '__v_isReadonly',
-  IS_SHALLOW = '__v_isShallow',
-  RAW = '__v_raw'
-}
-
-export interface Target {
-  __ob__?: Observer
-  [ReactiveFlags.SKIP]?: boolean
-  [ReactiveFlags.IS_READONLY]?: boolean
-  [ReactiveFlags.IS_SHALLOW]?: boolean
-  [ReactiveFlags.RAW]?: any
-}
-
-// only unwrap nested ref
-export type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRefSimple<T>
-
-export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
-export function reactive(target: object) {
-  makeReactive(target, false)
-  return target
-}
-
-export declare const ShallowReactiveMarker: unique symbol
-
-export type ShallowReactive<T> = T & { [ShallowReactiveMarker]?: true }
-
-/**
- * Return a shallowly-reactive copy of the original object, where only the root
- * level properties are reactive. It also does not auto-unwrap refs (even at the
- * root level).
- */
-export function shallowReactive<T extends object>(
-  target: T
-): ShallowReactive<T> {
-  makeReactive(target, true)
-  def(target, ReactiveFlags.IS_SHALLOW, true)
-  return target
-}
-
-function makeReactive(target: any, shallow: boolean) {
-  // if trying to observe a readonly proxy, return the readonly version.
-  if (!isReadonly(target)) {
-    if (__DEV__) {
-      if (isArray(target)) {
-        warn(
-          `Avoid using Array as root value for ${
-            shallow ? `shallowReactive()` : `reactive()`
-          } as it cannot be tracked in watch() or watchEffect(). Use ${
-            shallow ? `shallowRef()` : `ref()`
-          } instead. This is a Vue-2-only limitation.`
-        )
-      }
-      const existingOb = target && target.__ob__
-      if (existingOb && existingOb.shallow !== shallow) {
-        warn(
-          `Target is already a ${
-            existingOb.shallow ? `` : `non-`
-          }shallow reactive object, and cannot be converted to ${
-            shallow ? `` : `non-`
-          }shallow.`
-        )
-      }
-    }
-    const ob = observe(
-      target,
-      shallow,
-      isServerRendering() /* ssr mock reactivity */
-    )
-    if (__DEV__ && !ob) {
-      if (target == null || isPrimitive(target)) {
-        warn(`value cannot be made reactive: ${String(target)}`)
-      }
-      if (isCollectionType(target)) {
-        warn(
-          `Vue 2 does not support reactive collection types such as Map or Set.`
-        )
-      }
-    }
-  }
-}
-
-export function isReactive(value: unknown): boolean {
-  if (isReadonly(value)) {
-    return isReactive((value as Target)[ReactiveFlags.RAW])
-  }
-  return !!(value && (value as Target).__ob__)
-}
-
-export function isShallow(value: unknown): boolean {
-  return !!(value && (value as Target).__v_isShallow)
-}
-
-export function isReadonly(value: unknown): boolean {
-  return !!(value && (value as Target).__v_isReadonly)
-}
-
-export function isProxy(value: unknown): boolean {
-  return isReactive(value) || isReadonly(value)
-}
-
-export function toRaw<T>(observed: T): T {
-  const raw = observed && (observed as Target)[ReactiveFlags.RAW]
-  return raw ? toRaw(raw) : observed
-}
-
-export function markRaw<T extends object>(
-  value: T
-): T & { [RawSymbol]?: true } {
-  // non-extensible objects won't be observed anyway
-  if (Object.isExtensible(value)) {
-    def(value, ReactiveFlags.SKIP, true)
-  }
-  return value
-}
-
-/**
- * @internal
- */
-export function isCollectionType(value: unknown): boolean {
-  const type = toRawType(value)
-  return (
-    type === 'Map' || type === 'WeakMap' || type === 'Set' || type === 'WeakSet'
-  )
-}
diff --git a/src/v3/reactivity/readonly.ts b/src/v3/reactivity/readonly.ts
deleted file mode 100644
index e3a83930301..00000000000
--- a/src/v3/reactivity/readonly.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-import { def, warn, isPlainObject, isArray } from 'core/util'
-import {
-  isCollectionType,
-  isReadonly,
-  isShallow,
-  ReactiveFlags,
-  UnwrapNestedRefs
-} from './reactive'
-import { isRef, Ref, RefFlag } from './ref'
-
-type Primitive = string | number | boolean | bigint | symbol | undefined | null
-type Builtin = Primitive | Function | Date | Error | RegExp
-export type DeepReadonly<T> = T extends Builtin
-  ? T
-  : T extends Map<infer K, infer V>
-  ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
-  : T extends ReadonlyMap<infer K, infer V>
-  ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
-  : T extends WeakMap<infer K, infer V>
-  ? WeakMap<DeepReadonly<K>, DeepReadonly<V>>
-  : T extends Set<infer U>
-  ? ReadonlySet<DeepReadonly<U>>
-  : T extends ReadonlySet<infer U>
-  ? ReadonlySet<DeepReadonly<U>>
-  : T extends WeakSet<infer U>
-  ? WeakSet<DeepReadonly<U>>
-  : T extends Promise<infer U>
-  ? Promise<DeepReadonly<U>>
-  : T extends Ref<infer U>
-  ? Readonly<Ref<DeepReadonly<U>>>
-  : T extends {}
-  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
-  : Readonly<T>
-
-const rawToReadonlyFlag = `__v_rawToReadonly`
-const rawToShallowReadonlyFlag = `__v_rawToShallowReadonly`
-
-export function readonly<T extends object>(
-  target: T
-): DeepReadonly<UnwrapNestedRefs<T>> {
-  return createReadonly(target, false)
-}
-
-function createReadonly(target: any, shallow: boolean) {
-  if (!isPlainObject(target)) {
-    if (__DEV__) {
-      if (isArray(target)) {
-        warn(`Vue 2 does not support readonly arrays.`)
-      } else if (isCollectionType(target)) {
-        warn(
-          `Vue 2 does not support readonly collection types such as Map or Set.`
-        )
-      } else {
-        warn(`value cannot be made readonly: ${typeof target}`)
-      }
-    }
-    return target as any
-  }
-
-  if (__DEV__ && !Object.isExtensible(target)) {
-    warn(
-      `Vue 2 does not support creating readonly proxy for non-extensible object.`
-    )
-  }
-
-  // already a readonly object
-  if (isReadonly(target)) {
-    return target as any
-  }
-
-  // already has a readonly proxy
-  const existingFlag = shallow ? rawToShallowReadonlyFlag : rawToReadonlyFlag
-  const existingProxy = target[existingFlag]
-  if (existingProxy) {
-    return existingProxy
-  }
-
-  const proxy = Object.create(Object.getPrototypeOf(target))
-  def(target, existingFlag, proxy)
-
-  def(proxy, ReactiveFlags.IS_READONLY, true)
-  def(proxy, ReactiveFlags.RAW, target)
-
-  if (isRef(target)) {
-    def(proxy, RefFlag, true)
-  }
-  if (shallow || isShallow(target)) {
-    def(proxy, ReactiveFlags.IS_SHALLOW, true)
-  }
-
-  const keys = Object.keys(target)
-  for (let i = 0; i < keys.length; i++) {
-    defineReadonlyProperty(proxy, target, keys[i], shallow)
-  }
-
-  return proxy as any
-}
-
-function defineReadonlyProperty(
-  proxy: any,
-  target: any,
-  key: string,
-  shallow: boolean
-) {
-  Object.defineProperty(proxy, key, {
-    enumerable: true,
-    configurable: true,
-    get() {
-      const val = target[key]
-      return shallow || !isPlainObject(val) ? val : readonly(val)
-    },
-    set() {
-      __DEV__ &&
-        warn(`Set operation on key "${key}" failed: target is readonly.`)
-    }
-  })
-}
-
-/**
- * Returns a reactive-copy of the original object, where only the root level
- * properties are readonly, and does NOT unwrap refs nor recursively convert
- * returned properties.
- * This is used for creating the props proxy object for stateful components.
- */
-export function shallowReadonly<T extends object>(target: T): Readonly<T> {
-  return createReadonly(target, true)
-}
diff --git a/src/v3/reactivity/ref.ts b/src/v3/reactivity/ref.ts
deleted file mode 100644
index 33495806da1..00000000000
--- a/src/v3/reactivity/ref.ts
+++ /dev/null
@@ -1,293 +0,0 @@
-import { defineReactive } from 'core/observer/index'
-import {
-  isReactive,
-  ReactiveFlags,
-  type ShallowReactiveMarker
-} from './reactive'
-import type { IfAny } from 'types/utils'
-import Dep from 'core/observer/dep'
-import { warn, isArray, def, isServerRendering } from 'core/util'
-import { TrackOpTypes, TriggerOpTypes } from './operations'
-
-declare const RefSymbol: unique symbol
-export declare const RawSymbol: unique symbol
-
-/**
- * @internal
- */
-export const RefFlag = `__v_isRef`
-
-export interface Ref<T = any> {
-  value: T
-  /**
-   * Type differentiator only.
-   * We need this to be in public d.ts but don't want it to show up in IDE
-   * autocomplete, so we use a private Symbol instead.
-   */
-  [RefSymbol]: true
-  /**
-   * @internal
-   */
-  dep?: Dep
-  /**
-   * @internal
-   */
-  [RefFlag]: true
-}
-
-export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
-export function isRef(r: any): r is Ref {
-  return !!(r && (r as Ref).__v_isRef === true)
-}
-
-export function ref<T extends Ref>(value: T): T
-export function ref<T>(value: T): Ref<UnwrapRef<T>>
-export function ref<T = any>(): Ref<T | undefined>
-export function ref(value?: unknown) {
-  return createRef(value, false)
-}
-
-declare const ShallowRefMarker: unique symbol
-
-export type ShallowRef<T = any> = Ref<T> & { [ShallowRefMarker]?: true }
-
-export function shallowRef<T>(value: T | Ref<T>): Ref<T> | ShallowRef<T>
-export function shallowRef<T extends Ref>(value: T): T
-export function shallowRef<T>(value: T): ShallowRef<T>
-export function shallowRef<T = any>(): ShallowRef<T | undefined>
-export function shallowRef(value?: unknown) {
-  return createRef(value, true)
-}
-
-function createRef(rawValue: unknown, shallow: boolean) {
-  if (isRef(rawValue)) {
-    return rawValue
-  }
-  const ref: any = {}
-  def(ref, RefFlag, true)
-  def(ref, ReactiveFlags.IS_SHALLOW, shallow)
-  def(
-    ref,
-    'dep',
-    defineReactive(ref, 'value', rawValue, null, shallow, isServerRendering())
-  )
-  return ref
-}
-
-export function triggerRef(ref: Ref) {
-  if (__DEV__ && !ref.dep) {
-    warn(`received object is not a triggerable ref.`)
-  }
-  if (__DEV__) {
-    ref.dep &&
-      ref.dep.notify({
-        type: TriggerOpTypes.SET,
-        target: ref,
-        key: 'value'
-      })
-  } else {
-    ref.dep && ref.dep.notify()
-  }
-}
-
-export function unref<T>(ref: T | Ref<T>): T {
-  return isRef(ref) ? (ref.value as any) : ref
-}
-
-export function proxyRefs<T extends object>(
-  objectWithRefs: T
-): ShallowUnwrapRef<T> {
-  if (isReactive(objectWithRefs)) {
-    return objectWithRefs as any
-  }
-  const proxy = {}
-  const keys = Object.keys(objectWithRefs)
-  for (let i = 0; i < keys.length; i++) {
-    proxyWithRefUnwrap(proxy, objectWithRefs, keys[i])
-  }
-  return proxy as any
-}
-
-export function proxyWithRefUnwrap(
-  target: any,
-  source: Record<string, any>,
-  key: string
-) {
-  Object.defineProperty(target, key, {
-    enumerable: true,
-    configurable: true,
-    get: () => {
-      const val = source[key]
-      if (isRef(val)) {
-        return val.value
-      } else {
-        const ob = val && val.__ob__
-        if (ob) ob.dep.depend()
-        return val
-      }
-    },
-    set: value => {
-      const oldValue = source[key]
-      if (isRef(oldValue) && !isRef(value)) {
-        oldValue.value = value
-      } else {
-        source[key] = value
-      }
-    }
-  })
-}
-
-export type CustomRefFactory<T> = (
-  track: () => void,
-  trigger: () => void
-) => {
-  get: () => T
-  set: (value: T) => void
-}
-
-export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
-  const dep = new Dep()
-  const { get, set } = factory(
-    () => {
-      if (__DEV__) {
-        dep.depend({
-          target: ref,
-          type: TrackOpTypes.GET,
-          key: 'value'
-        })
-      } else {
-        dep.depend()
-      }
-    },
-    () => {
-      if (__DEV__) {
-        dep.notify({
-          target: ref,
-          type: TriggerOpTypes.SET,
-          key: 'value'
-        })
-      } else {
-        dep.notify()
-      }
-    }
-  )
-  const ref = {
-    get value() {
-      return get()
-    },
-    set value(newVal) {
-      set(newVal)
-    }
-  } as any
-  def(ref, RefFlag, true)
-  return ref
-}
-
-export type ToRefs<T = any> = {
-  [K in keyof T]: ToRef<T[K]>
-}
-
-export function toRefs<T extends object>(object: T): ToRefs<T> {
-  if (__DEV__ && !isReactive(object)) {
-    warn(`toRefs() expects a reactive object but received a plain one.`)
-  }
-  const ret: any = isArray(object) ? new Array(object.length) : {}
-  for (const key in object) {
-    ret[key] = toRef(object, key)
-  }
-  return ret
-}
-
-export type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>
-
-export function toRef<T extends object, K extends keyof T>(
-  object: T,
-  key: K
-): ToRef<T[K]>
-
-export function toRef<T extends object, K extends keyof T>(
-  object: T,
-  key: K,
-  defaultValue: T[K]
-): ToRef<Exclude<T[K], undefined>>
-
-export function toRef<T extends object, K extends keyof T>(
-  object: T,
-  key: K,
-  defaultValue?: T[K]
-): ToRef<T[K]> {
-  const val = object[key]
-  if (isRef(val)) {
-    return val as any
-  }
-  const ref = {
-    get value() {
-      const val = object[key]
-      return val === undefined ? (defaultValue as T[K]) : val
-    },
-    set value(newVal) {
-      object[key] = newVal
-    }
-  } as any
-  def(ref, RefFlag, true)
-  return ref
-}
-
-/**
- * This is a special exported interface for other packages to declare
- * additional types that should bail out for ref unwrapping. For example
- * \@vue/runtime-dom can declare it like so in its d.ts:
- *
- * ``` ts
- * declare module 'vue' {
- *   export interface RefUnwrapBailTypes {
- *     runtimeDOMBailTypes: Node | Window
- *   }
- * }
- * ```
- *
- * Note that api-extractor somehow refuses to include `declare module`
- * augmentations in its generated d.ts, so we have to manually append them
- * to the final generated d.ts in our build process.
- */
-export interface RefUnwrapBailTypes {
-  runtimeDOMBailTypes: Node | Window
-}
-
-export type ShallowUnwrapRef<T> = {
-  [K in keyof T]: T[K] extends Ref<infer V>
-    ? V
-    : // if `V` is `unknown` that means it does not extend `Ref` and is undefined
-    T[K] extends Ref<infer V> | undefined
-    ? unknown extends V
-      ? undefined
-      : V | undefined
-    : T[K]
-}
-
-export type UnwrapRef<T> = T extends ShallowRef<infer V>
-  ? V
-  : T extends Ref<infer V>
-  ? UnwrapRefSimple<V>
-  : UnwrapRefSimple<T>
-
-type BaseTypes = string | number | boolean
-type CollectionTypes = IterableCollections | WeakCollections
-type IterableCollections = Map<any, any> | Set<any>
-type WeakCollections = WeakMap<any, any> | WeakSet<any>
-
-export type UnwrapRefSimple<T> = T extends
-  | Function
-  | CollectionTypes
-  | BaseTypes
-  | Ref
-  | RefUnwrapBailTypes[keyof RefUnwrapBailTypes]
-  | { [RawSymbol]?: true }
-  ? T
-  : T extends Array<any>
-  ? { [K in keyof T]: UnwrapRefSimple<T[K]> }
-  : T extends object & { [ShallowReactiveMarker]?: never }
-  ? {
-      [P in keyof T]: P extends symbol ? T[P] : UnwrapRef<T[P]>
-    }
-  : T
diff --git a/src/v3/sfc-helpers/useCssModule.ts b/src/v3/sfc-helpers/useCssModule.ts
deleted file mode 100644
index df49931af12..00000000000
--- a/src/v3/sfc-helpers/useCssModule.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { emptyObject, warn } from '../../core/util'
-import { currentInstance } from '../currentInstance'
-
-export function useCssModule(name = '$style'): Record<string, string> {
-  /* istanbul ignore else */
-  if (!__GLOBAL__) {
-    if (!currentInstance) {
-      __DEV__ && warn(`useCssModule must be called inside setup()`)
-      return emptyObject
-    }
-    const mod = currentInstance[name]
-    if (!mod) {
-      __DEV__ &&
-        warn(`Current instance does not have CSS module named "${name}".`)
-      return emptyObject
-    }
-    return mod as Record<string, string>
-  } else {
-    if (__DEV__) {
-      warn(`useCssModule() is not supported in the global build.`)
-    }
-    return emptyObject
-  }
-}
diff --git a/src/v3/sfc-helpers/useCssVars.ts b/src/v3/sfc-helpers/useCssVars.ts
deleted file mode 100644
index cba7050a0e7..00000000000
--- a/src/v3/sfc-helpers/useCssVars.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { watchPostEffect } from '../'
-import { inBrowser, warn } from 'core/util'
-import { currentInstance } from '../currentInstance'
-
-/**
- * Runtime helper for SFC's CSS variable injection feature.
- * @private
- */
-export function useCssVars(
-  getter: (
-    vm: Record<string, any>,
-    setupProxy: Record<string, any>
-  ) => Record<string, string>
-) {
-  if (!inBrowser && !__TEST__) return
-
-  const instance = currentInstance
-  if (!instance) {
-    __DEV__ &&
-      warn(`useCssVars is called without current active component instance.`)
-    return
-  }
-
-  watchPostEffect(() => {
-    const el = instance.$el
-    const vars = getter(instance, instance._setupProxy!)
-    if (el && el.nodeType === 1) {
-      const style = (el as HTMLElement).style
-      for (const key in vars) {
-        style.setProperty(`--${key}`, vars[key])
-      }
-    }
-  })
-}
diff --git a/src/watcher.js b/src/watcher.js
new file mode 100644
index 00000000000..3b16c3211c4
--- /dev/null
+++ b/src/watcher.js
@@ -0,0 +1,364 @@
+import config from './config'
+import Dep from './observer/dep'
+import { parseExpression } from './parsers/expression'
+import { pushWatcher } from './batcher'
+import {
+  extend,
+  warn,
+  isArray,
+  isObject,
+  nextTick,
+  _Set as Set
+} from './util/index'
+
+let uid = 0
+
+/**
+ * A watcher parses an expression, collects dependencies,
+ * and fires callback when the expression value changes.
+ * This is used for both the $watch() api and directives.
+ *
+ * @param {Vue} vm
+ * @param {String|Function} expOrFn
+ * @param {Function} cb
+ * @param {Object} options
+ *                 - {Array} filters
+ *                 - {Boolean} twoWay
+ *                 - {Boolean} deep
+ *                 - {Boolean} user
+ *                 - {Boolean} sync
+ *                 - {Boolean} lazy
+ *                 - {Function} [preProcess]
+ *                 - {Function} [postProcess]
+ * @constructor
+ */
+
+export default function Watcher (vm, expOrFn, cb, options) {
+  // mix in options
+  if (options) {
+    extend(this, options)
+  }
+  var isFn = typeof expOrFn === 'function'
+  this.vm = vm
+  vm._watchers.push(this)
+  this.expression = expOrFn
+  this.cb = cb
+  this.id = ++uid // uid for batching
+  this.active = true
+  this.dirty = this.lazy // for lazy watchers
+  this.deps = []
+  this.newDeps = []
+  this.depIds = new Set()
+  this.newDepIds = new Set()
+  this.prevError = null // for async error stacks
+  // parse expression for getter/setter
+  if (isFn) {
+    this.getter = expOrFn
+    this.setter = undefined
+  } else {
+    var res = parseExpression(expOrFn, this.twoWay)
+    this.getter = res.get
+    this.setter = res.set
+  }
+  this.value = this.lazy
+    ? undefined
+    : this.get()
+  // state for avoiding false triggers for deep and Array
+  // watchers during vm._digest()
+  this.queued = this.shallow = false
+}
+
+/**
+ * Evaluate the getter, and re-collect dependencies.
+ */
+
+Watcher.prototype.get = function () {
+  this.beforeGet()
+  var scope = this.scope || this.vm
+  var value
+  try {
+    value = this.getter.call(scope, scope)
+  } catch (e) {
+    if (
+      process.env.NODE_ENV !== 'production' &&
+      config.warnExpressionErrors
+    ) {
+      warn(
+        'Error when evaluating expression ' +
+        '"' + this.expression + '": ' + e.toString(),
+        this.vm
+      )
+    }
+  }
+  // "touch" every property so they are all tracked as
+  // dependencies for deep watching
+  if (this.deep) {
+    traverse(value)
+  }
+  if (this.preProcess) {
+    value = this.preProcess(value)
+  }
+  if (this.filters) {
+    value = scope._applyFilters(value, null, this.filters, false)
+  }
+  if (this.postProcess) {
+    value = this.postProcess(value)
+  }
+  this.afterGet()
+  return value
+}
+
+/**
+ * Set the corresponding value with the setter.
+ *
+ * @param {*} value
+ */
+
+Watcher.prototype.set = function (value) {
+  var scope = this.scope || this.vm
+  if (this.filters) {
+    value = scope._applyFilters(
+      value, this.value, this.filters, true)
+  }
+  try {
+    this.setter.call(scope, scope, value)
+  } catch (e) {
+    if (
+      process.env.NODE_ENV !== 'production' &&
+      config.warnExpressionErrors
+    ) {
+      warn(
+        'Error when evaluating setter ' +
+        '"' + this.expression + '": ' + e.toString(),
+        this.vm
+      )
+    }
+  }
+  // two-way sync for v-for alias
+  var forContext = scope.$forContext
+  if (forContext && forContext.alias === this.expression) {
+    if (forContext.filters) {
+      process.env.NODE_ENV !== 'production' && warn(
+        'It seems you are using two-way binding on ' +
+        'a v-for alias (' + this.expression + '), and the ' +
+        'v-for has filters. This will not work properly. ' +
+        'Either remove the filters or use an array of ' +
+        'objects and bind to object properties instead.',
+        this.vm
+      )
+      return
+    }
+    forContext._withLock(function () {
+      if (scope.$key) { // original is an object
+        forContext.rawValue[scope.$key] = value
+      } else {
+        forContext.rawValue.$set(scope.$index, value)
+      }
+    })
+  }
+}
+
+/**
+ * Prepare for dependency collection.
+ */
+
+Watcher.prototype.beforeGet = function () {
+  Dep.target = this
+}
+
+/**
+ * Add a dependency to this directive.
+ *
+ * @param {Dep} dep
+ */
+
+Watcher.prototype.addDep = function (dep) {
+  var id = dep.id
+  if (!this.newDepIds.has(id)) {
+    this.newDepIds.add(id)
+    this.newDeps.push(dep)
+    if (!this.depIds.has(id)) {
+      dep.addSub(this)
+    }
+  }
+}
+
+/**
+ * Clean up for dependency collection.
+ */
+
+Watcher.prototype.afterGet = function () {
+  Dep.target = null
+  var i = this.deps.length
+  while (i--) {
+    var dep = this.deps[i]
+    if (!this.newDepIds.has(dep.id)) {
+      dep.removeSub(this)
+    }
+  }
+  var tmp = this.depIds
+  this.depIds = this.newDepIds
+  this.newDepIds = tmp
+  this.newDepIds.clear()
+  tmp = this.deps
+  this.deps = this.newDeps
+  this.newDeps = tmp
+  this.newDeps.length = 0
+}
+
+/**
+ * Subscriber interface.
+ * Will be called when a dependency changes.
+ *
+ * @param {Boolean} shallow
+ */
+
+Watcher.prototype.update = function (shallow) {
+  if (this.lazy) {
+    this.dirty = true
+  } else if (this.sync || !config.async) {
+    this.run()
+  } else {
+    // if queued, only overwrite shallow with non-shallow,
+    // but not the other way around.
+    this.shallow = this.queued
+      ? shallow
+        ? this.shallow
+        : false
+      : !!shallow
+    this.queued = true
+    // record before-push error stack in debug mode
+    /* istanbul ignore if */
+    if (process.env.NODE_ENV !== 'production' && config.debug) {
+      this.prevError = new Error('[vue] async stack trace')
+    }
+    pushWatcher(this)
+  }
+}
+
+/**
+ * Batcher job interface.
+ * Will be called by the batcher.
+ */
+
+Watcher.prototype.run = function () {
+  if (this.active) {
+    var value = this.get()
+    if (
+      value !== this.value ||
+      // Deep watchers and watchers on Object/Arrays should fire even
+      // when the value is the same, because the value may
+      // have mutated; but only do so if this is a
+      // non-shallow update (caused by a vm digest).
+      ((isObject(value) || this.deep) && !this.shallow)
+    ) {
+      // set new value
+      var oldValue = this.value
+      this.value = value
+      // in debug + async mode, when a watcher callbacks
+      // throws, we also throw the saved before-push error
+      // so the full cross-tick stack trace is available.
+      var prevError = this.prevError
+      /* istanbul ignore if */
+      if (process.env.NODE_ENV !== 'production' &&
+          config.debug && prevError) {
+        this.prevError = null
+        try {
+          this.cb.call(this.vm, value, oldValue)
+        } catch (e) {
+          nextTick(function () {
+            throw prevError
+          }, 0)
+          throw e
+        }
+      } else {
+        this.cb.call(this.vm, value, oldValue)
+      }
+    }
+    this.queued = this.shallow = false
+  }
+}
+
+/**
+ * Evaluate the value of the watcher.
+ * This only gets called for lazy watchers.
+ */
+
+Watcher.prototype.evaluate = function () {
+  // avoid overwriting another watcher that is being
+  // collected.
+  var current = Dep.target
+  this.value = this.get()
+  this.dirty = false
+  Dep.target = current
+}
+
+/**
+ * Depend on all deps collected by this watcher.
+ */
+
+Watcher.prototype.depend = function () {
+  var i = this.deps.length
+  while (i--) {
+    this.deps[i].depend()
+  }
+}
+
+/**
+ * Remove self from all dependencies' subcriber list.
+ */
+
+Watcher.prototype.teardown = function () {
+  if (this.active) {
+    // remove self from vm's watcher list
+    // this is a somewhat expensive operation so we skip it
+    // if the vm is being destroyed or is performing a v-for
+    // re-render (the watcher list is then filtered by v-for).
+    if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) {
+      this.vm._watchers.$remove(this)
+    }
+    var i = this.deps.length
+    while (i--) {
+      this.deps[i].removeSub(this)
+    }
+    this.active = false
+    this.vm = this.cb = this.value = null
+  }
+}
+
+/**
+ * Recrusively traverse an object to evoke all converted
+ * getters, so that every nested property inside the object
+ * is collected as a "deep" dependency.
+ *
+ * @param {*} val
+ */
+
+const seenObjects = new Set()
+function traverse (val, seen) {
+  let i, keys
+  if (!seen) {
+    seen = seenObjects
+    seen.clear()
+  }
+  const isA = isArray(val)
+  const isO = isObject(val)
+  if ((isA || isO) && Object.isExtensible(val)) {
+    if (val.__ob__) {
+      var depId = val.__ob__.dep.id
+      if (seen.has(depId)) {
+        return
+      } else {
+        seen.add(depId)
+      }
+    }
+    if (isA) {
+      i = val.length
+      while (i--) traverse(val[i], seen)
+    } else if (isO) {
+      keys = Object.keys(val)
+      i = keys.length
+      while (i--) traverse(val[keys[i]], seen)
+    }
+  }
+}
diff --git a/test/.eslintrc b/test/.eslintrc
new file mode 100644
index 00000000000..928c5769b23
--- /dev/null
+++ b/test/.eslintrc
@@ -0,0 +1,19 @@
+{
+  "parserOptions": {
+    "ecmaVersion": 5
+  },
+  "env": {
+    "jasmine": true
+  },
+  "globals": {
+    "$": true,
+    "jQuery": true,
+    "casper": true,
+    "getWarnCount": true
+  },
+  "rules": {
+    "no-multi-str": 0,
+    "object-curly-spacing": 0,
+    "array-bracket-spacing": 0
+  }
+}
diff --git a/test/e2e/async-edge-cases.html b/test/e2e/async-edge-cases.html
deleted file mode 100644
index 0fc9848efdb..00000000000
--- a/test/e2e/async-edge-cases.html
+++ /dev/null
@@ -1,53 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title></title>
-    <script src="../../dist/vue.min.js"></script>
-  </head>
-  <body>
-    <!-- #4510 click and change event on checkbox -->
-    <div id="case-1">
-      <div @click="num++">
-        {{ num }}
-        <input type="checkbox" v-model="checked">
-      </div>
-    </div>
-    <script>
-    var vm1 = new Vue({
-      el: '#case-1',
-      data: {
-        num: 1,
-        checked: false
-      }
-    })
-    </script>
-
-    <!-- #6566 click event bubbling -->
-    <div id="case-2">
-      <div class="panel" v-if="expand">
-        <button @click="expand = false, countA++">Expand is True</button>
-      </div>
-      <div class="header" v-if="!expand" @click="expand = true, countB++">
-        <button>Expand is False</button>
-      </div>
-      <div class="count-a">
-        countA: {{countA}}
-      </div>
-      <div class="count-b">
-        countB: {{countB}}
-      </div>
-    </div>
-    <script>
-    var vm2 = new Vue({
-      el: '#case-2',
-      data: {
-        expand: true,
-        countA: 0,
-        countB: 0,
-      }
-    })
-    </script>
-
-  </body>
-</html>
diff --git a/test/e2e/async-edge-cases.spec.ts b/test/e2e/async-edge-cases.spec.ts
deleted file mode 100644
index 08b1a751f35..00000000000
--- a/test/e2e/async-edge-cases.spec.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-// @vitest-environment node
-import path from 'path'
-import { E2E_TIMEOUT, setupPuppeteer } from './e2eUtils'
-
-describe('basic-ssr', () => {
-  const { page, text, click, isChecked } = setupPuppeteer()
-
-  test(
-    'should work',
-    async () => {
-      await page().goto(
-        `file://${path.resolve(__dirname, `async-edge-cases.html`)}`
-      )
-
-      // #4510
-      expect(await text('#case-1')).toContain('1')
-      expect(await isChecked('#case-1 input')).toBe(false)
-
-      await click('#case-1 input')
-      expect(await text('#case-1')).toContain('2')
-      expect(await isChecked('#case-1 input')).toBe(true)
-
-      await click('#case-1 input')
-      expect(await text('#case-1')).toContain('3')
-      expect(await isChecked('#case-1 input')).toBe(false)
-
-      // #6566
-      expect(await text('#case-2 button')).toContain('Expand is True')
-      expect(await text('.count-a')).toContain('countA: 0')
-      expect(await text('.count-b')).toContain('countB: 0')
-
-      await click('#case-2 button')
-      expect(await text('#case-2 button')).toContain('Expand is False')
-      expect(await text('.count-a')).toContain('countA: 1')
-      expect(await text('.count-b')).toContain('countB: 0')
-
-      await click('#case-2 button')
-      expect(await text('#case-2 button')).toContain('Expand is True')
-      expect(await text('.count-a')).toContain('countA: 1')
-      expect(await text('.count-b')).toContain('countB: 1')
-    },
-    E2E_TIMEOUT
-  )
-})
diff --git a/test/e2e/basic-ssr.html b/test/e2e/basic-ssr.html
deleted file mode 100644
index df6f57deaad..00000000000
--- a/test/e2e/basic-ssr.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <title></title>
-  </head>
-  <body>
-    <script src="../../dist/vue.min.js"></script>
-    <script src="../../packages/server-renderer/basic.js"></script>
-
-    <div id="result">wtf</div>
-
-    <script>
-    var vm = new Vue({
-      data: { msg: 'foo' },
-      template: '<div>{{ msg }}</div>'
-    })
-
-    renderVueComponentToString(vm, function (err, result) {
-      document.getElementById('result').textContent = err && err.toString() || result
-    })
-    </script>
-  </body>
-</html>
diff --git a/test/e2e/basic-ssr.spec.ts b/test/e2e/basic-ssr.spec.ts
deleted file mode 100644
index 384761644fe..00000000000
--- a/test/e2e/basic-ssr.spec.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-// @vitest-environment node
-import path from 'path'
-import { E2E_TIMEOUT, setupPuppeteer } from './e2eUtils'
-
-describe('basic-ssr', () => {
-  const { page, text } = setupPuppeteer()
-
-  test(
-    'should work',
-    async () => {
-      await page().goto(`file://${path.resolve(__dirname, `basic-ssr.html`)}`)
-      expect(await text('#result')).toContain(
-        `<div data-server-rendered="true">foo</div>`
-      )
-    },
-    E2E_TIMEOUT
-  )
-})
diff --git a/test/e2e/commits.js b/test/e2e/commits.js
new file mode 100644
index 00000000000..1b90e57eca3
--- /dev/null
+++ b/test/e2e/commits.js
@@ -0,0 +1,36 @@
+casper.on('remote.message', function (e) {
+  console.log(e)
+})
+
+casper.test.begin('commits', 18, function (test) {
+  casper
+  .start('examples/commits/index.html')
+  .then(function () {
+    // radio inputs & labels
+    test.assertElementCount('input', 2)
+    test.assertElementCount('label', 2)
+    test.assertSelectorHasText('label[for="master"]', 'master')
+    test.assertSelectorHasText('label[for="dev"]', 'dev')
+    // initial fetched commits
+    test.assertField('branch', 'master')
+    test.assertSelectorHasText('p', 'vuejs/vue@master')
+    test.assertElementCount('li', 3)
+    test.assertSelectorHasText('li:first-child a.commit', '1111111')
+    test.assertSelectorHasText('li:first-child span.message', 'one')
+    test.assertSelectorHasText('li:first-child span.author', 'Evan')
+    test.assertSelectorHasText('li:first-child span.date', '2014-10-15 13:52:58')
+  })
+  .thenClick('input[value="dev"]', function () {
+    test.assertField('branch', 'dev')
+    test.assertSelectorHasText('p', 'vuejs/vue@dev')
+    test.assertElementCount('li', 3)
+    test.assertSelectorHasText('li:first-child a.commit', '2222222')
+    test.assertSelectorHasText('li:first-child span.message', 'two')
+    test.assertSelectorHasText('li:first-child span.author', 'Evan')
+    test.assertSelectorHasText('li:first-child span.date', '2014-10-15 13:52:58')
+  })
+  // run
+  .run(function () {
+    test.done()
+  })
+})
diff --git a/test/e2e/commits.mock.ts b/test/e2e/commits.mock.ts
deleted file mode 100644
index c13e127d736..00000000000
--- a/test/e2e/commits.mock.ts
+++ /dev/null
@@ -1,551 +0,0 @@
-export default {
-  main: [
-    {
-      sha: '0948d999f2fddf9f90991956493f976273c5da1f',
-      node_id:
-        'MDY6Q29tbWl0MTE3MzAzNDI6MDk0OGQ5OTlmMmZkZGY5ZjkwOTkxOTU2NDkzZjk3NjI3M2M1ZGExZg==',
-      commit: {
-        author: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2017-10-13T03:07:14Z'
-        },
-        committer: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2017-10-13T03:07:14Z'
-        },
-        message: 'build: release 2.5.0',
-        tree: {
-          sha: '7846816b875eb664ddf718fad04a720efeac72d0',
-          url: 'https://api.github.com/repos/vuejs/vue/git/trees/7846816b875eb664ddf718fad04a720efeac72d0'
-        },
-        url: 'https://api.github.com/repos/vuejs/vue/git/commits/0948d999f2fddf9f90991956493f976273c5da1f',
-        comment_count: 0,
-        verification: {
-          verified: false,
-          reason: 'unsigned',
-          signature: null,
-          payload: null
-        }
-      },
-      url: 'https://api.github.com/repos/vuejs/vue/commits/0948d999f2fddf9f90991956493f976273c5da1f',
-      html_url:
-        'https://github.com/vuejs/vue/commit/0948d999f2fddf9f90991956493f976273c5da1f',
-      comments_url:
-        'https://api.github.com/repos/vuejs/vue/commits/0948d999f2fddf9f90991956493f976273c5da1f/comments',
-      author: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      committer: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      parents: [
-        {
-          sha: 'bc2918f0e596d0e133a25606cbb66075402ce6c3',
-          url: 'https://api.github.com/repos/vuejs/vue/commits/bc2918f0e596d0e133a25606cbb66075402ce6c3',
-          html_url:
-            'https://github.com/vuejs/vue/commit/bc2918f0e596d0e133a25606cbb66075402ce6c3'
-        }
-      ]
-    },
-    {
-      sha: 'bc2918f0e596d0e133a25606cbb66075402ce6c3',
-      node_id:
-        'MDY6Q29tbWl0MTE3MzAzNDI6YmMyOTE4ZjBlNTk2ZDBlMTMzYTI1NjA2Y2JiNjYwNzU0MDJjZTZjMw==',
-      commit: {
-        author: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2017-10-13T03:04:35Z'
-        },
-        committer: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2017-10-13T03:04:35Z'
-        },
-        message: 'build: build 2.5.0',
-        tree: {
-          sha: '5c57af855d76df68ec0782a2d2f4cd0a54e80125',
-          url: 'https://api.github.com/repos/vuejs/vue/git/trees/5c57af855d76df68ec0782a2d2f4cd0a54e80125'
-        },
-        url: 'https://api.github.com/repos/vuejs/vue/git/commits/bc2918f0e596d0e133a25606cbb66075402ce6c3',
-        comment_count: 0,
-        verification: {
-          verified: false,
-          reason: 'unsigned',
-          signature: null,
-          payload: null
-        }
-      },
-      url: 'https://api.github.com/repos/vuejs/vue/commits/bc2918f0e596d0e133a25606cbb66075402ce6c3',
-      html_url:
-        'https://github.com/vuejs/vue/commit/bc2918f0e596d0e133a25606cbb66075402ce6c3',
-      comments_url:
-        'https://api.github.com/repos/vuejs/vue/commits/bc2918f0e596d0e133a25606cbb66075402ce6c3/comments',
-      author: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      committer: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      parents: [
-        {
-          sha: 'df8f179cfc3b98d6e0f48502cc5071b993d9cdb5',
-          url: 'https://api.github.com/repos/vuejs/vue/commits/df8f179cfc3b98d6e0f48502cc5071b993d9cdb5',
-          html_url:
-            'https://github.com/vuejs/vue/commit/df8f179cfc3b98d6e0f48502cc5071b993d9cdb5'
-        }
-      ]
-    },
-    {
-      sha: 'df8f179cfc3b98d6e0f48502cc5071b993d9cdb5',
-      node_id:
-        'MDY6Q29tbWl0MTE3MzAzNDI6ZGY4ZjE3OWNmYzNiOThkNmUwZjQ4NTAyY2M1MDcxYjk5M2Q5Y2RiNQ==',
-      commit: {
-        author: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2017-10-13T00:41:36Z'
-        },
-        committer: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2017-10-13T00:41:36Z'
-        },
-        message: 'test: make hydration spec more stable for Edge',
-        tree: {
-          sha: 'b399dba6180378d6a04715a5624599b49b3e6454',
-          url: 'https://api.github.com/repos/vuejs/vue/git/trees/b399dba6180378d6a04715a5624599b49b3e6454'
-        },
-        url: 'https://api.github.com/repos/vuejs/vue/git/commits/df8f179cfc3b98d6e0f48502cc5071b993d9cdb5',
-        comment_count: 0,
-        verification: {
-          verified: false,
-          reason: 'unsigned',
-          signature: null,
-          payload: null
-        }
-      },
-      url: 'https://api.github.com/repos/vuejs/vue/commits/df8f179cfc3b98d6e0f48502cc5071b993d9cdb5',
-      html_url:
-        'https://github.com/vuejs/vue/commit/df8f179cfc3b98d6e0f48502cc5071b993d9cdb5',
-      comments_url:
-        'https://api.github.com/repos/vuejs/vue/commits/df8f179cfc3b98d6e0f48502cc5071b993d9cdb5/comments',
-      author: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      committer: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      parents: [
-        {
-          sha: 'a85f95c422e0bde6ce4068f5e44e761d4e00ca08',
-          url: 'https://api.github.com/repos/vuejs/vue/commits/a85f95c422e0bde6ce4068f5e44e761d4e00ca08',
-          html_url:
-            'https://github.com/vuejs/vue/commit/a85f95c422e0bde6ce4068f5e44e761d4e00ca08'
-        }
-      ]
-    }
-  ],
-  dev: [
-    {
-      sha: '4074104fac219e61e542f4da3a4800975a8063f2',
-      node_id:
-        'MDY6Q29tbWl0MTE3MzAzNDI6NDA3NDEwNGZhYzIxOWU2MWU1NDJmNGRhM2E0ODAwOTc1YTgwNjNmMg==',
-      commit: {
-        author: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2018-12-11T21:51:40Z'
-        },
-        committer: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2018-12-11T21:51:40Z'
-        },
-        message: 'perf: skip normalization on single child element v-for',
-        tree: {
-          sha: '75b999a0562d64a38eb322973c982edfa8d84fda',
-          url: 'https://api.github.com/repos/vuejs/vue/git/trees/75b999a0562d64a38eb322973c982edfa8d84fda'
-        },
-        url: 'https://api.github.com/repos/vuejs/vue/git/commits/4074104fac219e61e542f4da3a4800975a8063f2',
-        comment_count: 0,
-        verification: {
-          verified: false,
-          reason: 'unsigned',
-          signature: null,
-          payload: null
-        }
-      },
-      url: 'https://api.github.com/repos/vuejs/vue/commits/4074104fac219e61e542f4da3a4800975a8063f2',
-      html_url:
-        'https://github.com/vuejs/vue/commit/4074104fac219e61e542f4da3a4800975a8063f2',
-      comments_url:
-        'https://api.github.com/repos/vuejs/vue/commits/4074104fac219e61e542f4da3a4800975a8063f2/comments',
-      author: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      committer: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      parents: [
-        {
-          sha: '47487607fbb99339038cf84990ba341c25b5e20d',
-          url: 'https://api.github.com/repos/vuejs/vue/commits/47487607fbb99339038cf84990ba341c25b5e20d',
-          html_url:
-            'https://github.com/vuejs/vue/commit/47487607fbb99339038cf84990ba341c25b5e20d'
-        }
-      ]
-    },
-    {
-      sha: '47487607fbb99339038cf84990ba341c25b5e20d',
-      node_id:
-        'MDY6Q29tbWl0MTE3MzAzNDI6NDc0ODc2MDdmYmI5OTMzOTAzOGNmODQ5OTBiYTM0MWMyNWI1ZTIwZA==',
-      commit: {
-        author: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2018-12-11T21:51:03Z'
-        },
-        committer: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2018-12-11T21:51:03Z'
-        },
-        message: 'fix: fix v-for component with undefined value\n\nfix #9181',
-        tree: {
-          sha: 'cc30183c2663cd88a35a4a18f758ad0ca872805a',
-          url: 'https://api.github.com/repos/vuejs/vue/git/trees/cc30183c2663cd88a35a4a18f758ad0ca872805a'
-        },
-        url: 'https://api.github.com/repos/vuejs/vue/git/commits/47487607fbb99339038cf84990ba341c25b5e20d',
-        comment_count: 0,
-        verification: {
-          verified: false,
-          reason: 'unsigned',
-          signature: null,
-          payload: null
-        }
-      },
-      url: 'https://api.github.com/repos/vuejs/vue/commits/47487607fbb99339038cf84990ba341c25b5e20d',
-      html_url:
-        'https://github.com/vuejs/vue/commit/47487607fbb99339038cf84990ba341c25b5e20d',
-      comments_url:
-        'https://api.github.com/repos/vuejs/vue/commits/47487607fbb99339038cf84990ba341c25b5e20d/comments',
-      author: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      committer: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      parents: [
-        {
-          sha: '984393fed981c58ad79ed50424f023dcfa6829d0',
-          url: 'https://api.github.com/repos/vuejs/vue/commits/984393fed981c58ad79ed50424f023dcfa6829d0',
-          html_url:
-            'https://github.com/vuejs/vue/commit/984393fed981c58ad79ed50424f023dcfa6829d0'
-        }
-      ]
-    },
-    {
-      sha: '984393fed981c58ad79ed50424f023dcfa6829d0',
-      node_id:
-        'MDY6Q29tbWl0MTE3MzAzNDI6OTg0MzkzZmVkOTgxYzU4YWQ3OWVkNTA0MjRmMDIzZGNmYTY4MjlkMA==',
-      commit: {
-        author: {
-          name: 'krystal',
-          email: 'krystalnumber@gmail.com',
-          date: '2018-12-11T16:37:39Z'
-        },
-        committer: {
-          name: 'Evan You',
-          email: 'yyx990803@gmail.com',
-          date: '2018-12-11T16:37:39Z'
-        },
-        message: "test: change model text's priority case (#9170)",
-        tree: {
-          sha: '9af5d03838b964ea98c3173c92c3e6e5263ee9ec',
-          url: 'https://api.github.com/repos/vuejs/vue/git/trees/9af5d03838b964ea98c3173c92c3e6e5263ee9ec'
-        },
-        url: 'https://api.github.com/repos/vuejs/vue/git/commits/984393fed981c58ad79ed50424f023dcfa6829d0',
-        comment_count: 0,
-        verification: {
-          verified: false,
-          reason: 'unsigned',
-          signature: null,
-          payload: null
-        }
-      },
-      url: 'https://api.github.com/repos/vuejs/vue/commits/984393fed981c58ad79ed50424f023dcfa6829d0',
-      html_url:
-        'https://github.com/vuejs/vue/commit/984393fed981c58ad79ed50424f023dcfa6829d0',
-      comments_url:
-        'https://api.github.com/repos/vuejs/vue/commits/984393fed981c58ad79ed50424f023dcfa6829d0/comments',
-      author: {
-        login: 'dejour',
-        id: 7224044,
-        node_id: 'MDQ6VXNlcjcyMjQwNDQ=',
-        avatar_url: 'https://avatars3.githubusercontent.com/u/7224044?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/dejour',
-        html_url: 'https://github.com/dejour',
-        followers_url: 'https://api.github.com/users/dejour/followers',
-        following_url:
-          'https://api.github.com/users/dejour/following{/other_user}',
-        gists_url: 'https://api.github.com/users/dejour/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/dejour/starred{/owner}{/repo}',
-        subscriptions_url: 'https://api.github.com/users/dejour/subscriptions',
-        organizations_url: 'https://api.github.com/users/dejour/orgs',
-        repos_url: 'https://api.github.com/users/dejour/repos',
-        events_url: 'https://api.github.com/users/dejour/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/dejour/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      committer: {
-        login: 'yyx990803',
-        id: 499550,
-        node_id: 'MDQ6VXNlcjQ5OTU1MA==',
-        avatar_url: 'https://avatars1.githubusercontent.com/u/499550?v=4',
-        gravatar_id: '',
-        url: 'https://api.github.com/users/yyx990803',
-        html_url: 'https://github.com/yyx990803',
-        followers_url: 'https://api.github.com/users/yyx990803/followers',
-        following_url:
-          'https://api.github.com/users/yyx990803/following{/other_user}',
-        gists_url: 'https://api.github.com/users/yyx990803/gists{/gist_id}',
-        starred_url:
-          'https://api.github.com/users/yyx990803/starred{/owner}{/repo}',
-        subscriptions_url:
-          'https://api.github.com/users/yyx990803/subscriptions',
-        organizations_url: 'https://api.github.com/users/yyx990803/orgs',
-        repos_url: 'https://api.github.com/users/yyx990803/repos',
-        events_url: 'https://api.github.com/users/yyx990803/events{/privacy}',
-        received_events_url:
-          'https://api.github.com/users/yyx990803/received_events',
-        type: 'User',
-        site_admin: false
-      },
-      parents: [
-        {
-          sha: '6980035a86cfb79368af77a5040e468177d6b14a',
-          url: 'https://api.github.com/repos/vuejs/vue/commits/6980035a86cfb79368af77a5040e468177d6b14a',
-          html_url:
-            'https://github.com/vuejs/vue/commit/6980035a86cfb79368af77a5040e468177d6b14a'
-        }
-      ]
-    }
-  ]
-}
diff --git a/test/e2e/commits.spec.ts b/test/e2e/commits.spec.ts
deleted file mode 100644
index 28796fe7c71..00000000000
--- a/test/e2e/commits.spec.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-// @vitest-environment node
-import { setupPuppeteer, getExampleUrl, E2E_TIMEOUT } from './e2eUtils'
-import mocks from './commits.mock'
-
-describe('e2e: commits', () => {
-  const { page, click, count, text, isChecked } = setupPuppeteer()
-
-  async function testCommits(apiType: 'classic' | 'composition') {
-    // intercept and mock the response to avoid hitting the actual API
-    await page().setRequestInterception(true)
-    page().on('request', req => {
-      const match = req.url().match(/&sha=(.*)$/)
-      if (!match) {
-        req.continue()
-      } else {
-        const ret = JSON.stringify(mocks[match[1] as 'main' | 'dev'])
-        req.respond({
-          status: 200,
-          contentType: 'application/json',
-          headers: { 'Access-Control-Allow-Origin': '*' },
-          body: ret
-        })
-      }
-    })
-
-    await page().goto(getExampleUrl('commits', apiType))
-    await page().waitForSelector('li')
-
-    expect(await count('input')).toBe(2)
-    expect(await count('label')).toBe(2)
-    expect(await text('label[for="main"]')).toBe('main')
-    expect(await text('label[for="dev"]')).toBe('dev')
-    expect(await isChecked('#main')).toBe(true)
-    expect(await isChecked('#dev')).toBe(false)
-    expect(await text('p')).toBe('vuejs/vue@main')
-    expect(await count('li')).toBe(3)
-    expect(await count('li .commit')).toBe(3)
-    expect(await count('li .message')).toBe(3)
-
-    await click('#dev')
-    expect(await text('p')).toBe('vuejs/vue@dev')
-    expect(await count('li')).toBe(3)
-    expect(await count('li .commit')).toBe(3)
-    expect(await count('li .message')).toBe(3)
-  }
-
-  test(
-    'classic',
-    async () => {
-      await testCommits('classic')
-    },
-    E2E_TIMEOUT
-  )
-
-  test(
-    'composition',
-    async () => {
-      await testCommits('composition')
-    },
-    E2E_TIMEOUT
-  )
-})
diff --git a/test/e2e/e2eUtils.ts b/test/e2e/e2eUtils.ts
deleted file mode 100644
index b991cbd299a..00000000000
--- a/test/e2e/e2eUtils.ts
+++ /dev/null
@@ -1,187 +0,0 @@
-import path from 'path'
-import puppeteer from 'puppeteer'
-
-export function getExampleUrl(
-  name: string,
-  apiType: 'classic' | 'composition'
-) {
-  const file = apiType === 'composition' ? `${name}.html` : `${name}/index.html`
-  return `file://${path.resolve(
-    __dirname,
-    `../../examples/${apiType}/${file}`
-  )}`
-}
-
-export const E2E_TIMEOUT = 30 * 1000
-
-const puppeteerOptions = process.env.CI
-  ? { args: ['--no-sandbox', '--disable-setuid-sandbox'] }
-  : { headless: !process.env.DEBUG }
-
-const maxTries = 30
-export const timeout = (n: number) => new Promise(r => setTimeout(r, n))
-
-export async function expectByPolling(
-  poll: () => Promise<any>,
-  expected: string
-) {
-  for (let tries = 0; tries < maxTries; tries++) {
-    const actual = (await poll()) || ''
-    if (actual.indexOf(expected) > -1 || tries === maxTries - 1) {
-      expect(actual).toMatch(expected)
-      break
-    } else {
-      await timeout(50)
-    }
-  }
-}
-
-export function setupPuppeteer() {
-  let browser: puppeteer.Browser
-  let page: puppeteer.Page
-
-  beforeAll(async () => {
-    browser = await puppeteer.launch(puppeteerOptions)
-  })
-
-  beforeEach(async () => {
-    page = await browser.newPage()
-
-    await page.evaluateOnNewDocument(() => {
-      localStorage.clear()
-    })
-
-    page.on('console', e => {
-      if (e.type() === 'error') {
-        const err = e.args()[0]
-        console.error(
-          `Error from Puppeteer-loaded page:\n`,
-          err._remoteObject.description
-        )
-      }
-    })
-  })
-
-  afterEach(async () => {
-    await page.close()
-  })
-
-  afterAll(async () => {
-    await browser.close()
-  })
-
-  async function click(selector: string, options?: puppeteer.ClickOptions) {
-    await page.click(selector, options)
-  }
-
-  async function count(selector: string) {
-    return (await page.$$(selector)).length
-  }
-
-  async function text(selector: string) {
-    return await page.$eval(selector, node => node.textContent)
-  }
-
-  async function value(selector: string) {
-    return await page.$eval(selector, node => (node as HTMLInputElement).value)
-  }
-
-  async function html(selector: string) {
-    return await page.$eval(selector, node => node.innerHTML)
-  }
-
-  async function classList(selector: string) {
-    return await page.$eval(selector, (node: any) => [...node.classList])
-  }
-
-  async function childrenCount(selector: string) {
-    return await page.$eval(selector, (node: any) => node.children.length)
-  }
-
-  async function isVisible(selector: string) {
-    const display = await page.$eval(selector, node => {
-      return window.getComputedStyle(node).display
-    })
-    return display !== 'none'
-  }
-
-  async function isChecked(selector: string) {
-    return await page.$eval(
-      selector,
-      node => (node as HTMLInputElement).checked
-    )
-  }
-
-  async function isFocused(selector: string) {
-    return await page.$eval(selector, node => node === document.activeElement)
-  }
-
-  async function setValue(selector: string, value: string) {
-    await page.$eval(
-      selector,
-      (node, value) => {
-        ;(node as HTMLInputElement).value = value as string
-        node.dispatchEvent(new Event('input'))
-      },
-      value
-    )
-  }
-
-  async function typeValue(selector: string, value: string) {
-    const el = (await page.$(selector))!
-    await el.evaluate(node => ((node as HTMLInputElement).value = ''))
-    await el.type(value)
-  }
-
-  async function enterValue(selector: string, value: string) {
-    const el = (await page.$(selector))!
-    await el.evaluate(node => ((node as HTMLInputElement).value = ''))
-    await el.type(value)
-    await el.press('Enter')
-  }
-
-  async function clearValue(selector: string) {
-    return await page.$eval(
-      selector,
-      node => ((node as HTMLInputElement).value = '')
-    )
-  }
-
-  function timeout(time: number) {
-    return page.evaluate(time => {
-      return new Promise(r => {
-        setTimeout(r, time)
-      })
-    }, time)
-  }
-
-  function nextFrame() {
-    return page.evaluate(() => {
-      return new Promise(resolve => {
-        requestAnimationFrame(() => {
-          requestAnimationFrame(resolve)
-        })
-      })
-    })
-  }
-
-  return {
-    page: () => page,
-    click,
-    count,
-    text,
-    value,
-    html,
-    classList,
-    childrenCount,
-    isVisible,
-    isChecked,
-    isFocused,
-    setValue,
-    typeValue,
-    enterValue,
-    clearValue,
-    timeout,
-    nextFrame
-  }
-}
diff --git a/test/e2e/grid.js b/test/e2e/grid.js
new file mode 100644
index 00000000000..873fe248351
--- /dev/null
+++ b/test/e2e/grid.js
@@ -0,0 +1,112 @@
+casper.test.begin('grid', 73, function (test) {
+  casper
+  .start('examples/grid/index.html')
+  .then(function () {
+    // headers
+    test.assertElementCount('th', 2)
+    test.assertElementCount('th.active', 0)
+    test.assertSelectorHasText('th:nth-child(1)', 'Name')
+    test.assertSelectorHasText('th:nth-child(2)', 'Power')
+    assertTable(test, ['name', 'power'], [
+      { name: 'Chuck Norris', power: Infinity },
+      { name: 'Bruce Lee', power: 9000 },
+      { name: 'Jackie Chan', power: 7000 },
+      { name: 'Jet Li', power: 8000 }
+    ])
+  })
+  // test sorting
+  .thenClick('th:nth-child(1)', function () {
+    test.assertElementCount('th.active:nth-child(1)', 1)
+    test.assertElementCount('th.active:nth-child(2)', 0)
+    test.assertElementCount('th:nth-child(1) .arrow.dsc', 1)
+    test.assertElementCount('th:nth-child(2) .arrow.dsc', 0)
+    assertTable(test, ['name', 'power'], [
+      { name: 'Jet Li', power: 8000 },
+      { name: 'Jackie Chan', power: 7000 },
+      { name: 'Chuck Norris', power: Infinity },
+      { name: 'Bruce Lee', power: 9000 }
+    ])
+  })
+  .thenClick('th:nth-child(2)', function () {
+    test.assertElementCount('th.active:nth-child(1)', 0)
+    test.assertElementCount('th.active:nth-child(2)', 1)
+    test.assertElementCount('th:nth-child(1) .arrow.dsc', 1)
+    test.assertElementCount('th:nth-child(2) .arrow.dsc', 1)
+    assertTable(test, ['name', 'power'], [
+      { name: 'Chuck Norris', power: Infinity },
+      { name: 'Bruce Lee', power: 9000 },
+      { name: 'Jet Li', power: 8000 },
+      { name: 'Jackie Chan', power: 7000 }
+    ])
+  })
+  .thenClick('th:nth-child(2)', function () {
+    test.assertElementCount('th.active:nth-child(1)', 0)
+    test.assertElementCount('th.active:nth-child(2)', 1)
+    test.assertElementCount('th:nth-child(1) .arrow.dsc', 1)
+    test.assertElementCount('th:nth-child(2) .arrow.asc', 1)
+    assertTable(test, ['name', 'power'], [
+      { name: 'Jackie Chan', power: 7000 },
+      { name: 'Jet Li', power: 8000 },
+      { name: 'Bruce Lee', power: 9000 },
+      { name: 'Chuck Norris', power: Infinity }
+    ])
+  })
+  .thenClick('th:nth-child(1)', function () {
+    test.assertElementCount('th.active:nth-child(1)', 1)
+    test.assertElementCount('th.active:nth-child(2)', 0)
+    test.assertElementCount('th:nth-child(1) .arrow.asc', 1)
+    test.assertElementCount('th:nth-child(2) .arrow.asc', 1)
+    assertTable(test, ['name', 'power'], [
+      { name: 'Bruce Lee', power: 9000 },
+      { name: 'Chuck Norris', power: Infinity },
+      { name: 'Jackie Chan', power: 7000 },
+      { name: 'Jet Li', power: 8000 }
+    ])
+  })
+  // test search
+  .then(function () {
+    this.fill('#search', {
+      query: 'j'
+    })
+  })
+  .then(function () {
+    assertTable(test, ['name', 'power'], [
+      { name: 'Jackie Chan', power: 7000 },
+      { name: 'Jet Li', power: 8000 }
+    ])
+  })
+  .then(function () {
+    this.fill('#search', {
+      query: 'infinity'
+    })
+  })
+  .then(function () {
+    assertTable(test, ['name', 'power'], [
+      { name: 'Chuck Norris', power: Infinity }
+    ])
+  })
+  // run
+  .run(function () {
+    test.done()
+  })
+
+  /**
+   * Helper to assert the table data is rendered correctly.
+   *
+   * @param {CasperTester} test
+   * @param {Array} columns
+   * @param {Array} data
+   */
+
+  function assertTable (test, columns, data) {
+    test.assertElementCount('td', data.length * columns.length)
+    for (var i = 0; i < data.length; i++) {
+      for (var j = 0; j < columns.length; j++) {
+        test.assertSelectorHasText(
+          'tr:nth-child(' + (i + 1) + ') td:nth-child(' + (j + 1) + ')',
+          data[i][columns[j]]
+        )
+      }
+    }
+  }
+})
diff --git a/test/e2e/grid.spec.ts b/test/e2e/grid.spec.ts
deleted file mode 100644
index 937a5bbb2dd..00000000000
--- a/test/e2e/grid.spec.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import { setupPuppeteer, getExampleUrl, E2E_TIMEOUT } from './e2eUtils'
-
-interface TableData {
-  name: string
-  power: number
-}
-
-describe('e2e: grid', () => {
-  const { page, click, text, count, typeValue, clearValue } = setupPuppeteer()
-  const columns = ['name', 'power'] as const
-
-  async function assertTable(data: TableData[]) {
-    expect(await count('td')).toBe(data.length * columns.length)
-    for (let i = 0; i < data.length; i++) {
-      for (let j = 0; j < columns.length; j++) {
-        expect(
-          await text(`tr:nth-child(${i + 1}) td:nth-child(${j + 1})`)
-        ).toContain(`${data[i][columns[j]]}`)
-      }
-    }
-  }
-
-  async function testGrid(apiType: 'classic' | 'composition') {
-    await page().goto(getExampleUrl('grid', apiType))
-    await page().waitForSelector('table')
-    expect(await count('th')).toBe(2)
-    expect(await count('th.active')).toBe(0)
-    expect(await text('th:nth-child(1)')).toContain('Name')
-    expect(await text('th:nth-child(2)')).toContain('Power')
-    await assertTable([
-      { name: 'Chuck Norris', power: Infinity },
-      { name: 'Bruce Lee', power: 9000 },
-      { name: 'Jackie Chan', power: 7000 },
-      { name: 'Jet Li', power: 8000 }
-    ])
-
-    await click('th:nth-child(1)')
-    expect(await count('th.active:nth-child(1)')).toBe(1)
-    expect(await count('th.active:nth-child(2)')).toBe(0)
-    expect(await count('th:nth-child(1) .arrow.dsc')).toBe(1)
-    expect(await count('th:nth-child(2) .arrow.dsc')).toBe(0)
-    await assertTable([
-      { name: 'Jet Li', power: 8000 },
-      { name: 'Jackie Chan', power: 7000 },
-      { name: 'Chuck Norris', power: Infinity },
-      { name: 'Bruce Lee', power: 9000 }
-    ])
-
-    await click('th:nth-child(2)')
-    expect(await count('th.active:nth-child(1)')).toBe(0)
-    expect(await count('th.active:nth-child(2)')).toBe(1)
-    expect(await count('th:nth-child(1) .arrow.dsc')).toBe(1)
-    expect(await count('th:nth-child(2) .arrow.dsc')).toBe(1)
-    await assertTable([
-      { name: 'Chuck Norris', power: Infinity },
-      { name: 'Bruce Lee', power: 9000 },
-      { name: 'Jet Li', power: 8000 },
-      { name: 'Jackie Chan', power: 7000 }
-    ])
-
-    await click('th:nth-child(2)')
-    expect(await count('th.active:nth-child(1)')).toBe(0)
-    expect(await count('th.active:nth-child(2)')).toBe(1)
-    expect(await count('th:nth-child(1) .arrow.dsc')).toBe(1)
-    expect(await count('th:nth-child(2) .arrow.asc')).toBe(1)
-    await assertTable([
-      { name: 'Jackie Chan', power: 7000 },
-      { name: 'Jet Li', power: 8000 },
-      { name: 'Bruce Lee', power: 9000 },
-      { name: 'Chuck Norris', power: Infinity }
-    ])
-
-    await click('th:nth-child(1)')
-    expect(await count('th.active:nth-child(1)')).toBe(1)
-    expect(await count('th.active:nth-child(2)')).toBe(0)
-    expect(await count('th:nth-child(1) .arrow.asc')).toBe(1)
-    expect(await count('th:nth-child(2) .arrow.asc')).toBe(1)
-    await assertTable([
-      { name: 'Bruce Lee', power: 9000 },
-      { name: 'Chuck Norris', power: Infinity },
-      { name: 'Jackie Chan', power: 7000 },
-      { name: 'Jet Li', power: 8000 }
-    ])
-
-    await typeValue('input[name="query"]', 'j')
-    await assertTable([
-      { name: 'Jackie Chan', power: 7000 },
-      { name: 'Jet Li', power: 8000 }
-    ])
-
-    await typeValue('input[name="query"]', 'infinity')
-    await assertTable([{ name: 'Chuck Norris', power: Infinity }])
-
-    await clearValue('input[name="query"]')
-    expect(await count('p')).toBe(0)
-    await typeValue('input[name="query"]', 'stringthatdoesnotexistanywhere')
-    expect(await count('p')).toBe(1)
-  }
-
-  test(
-    'classic',
-    async () => {
-      await testGrid('classic')
-    },
-    E2E_TIMEOUT
-  )
-
-  test(
-    'composition',
-    async () => {
-      await testGrid('composition')
-    },
-    E2E_TIMEOUT
-  )
-})
diff --git a/test/e2e/markdown.js b/test/e2e/markdown.js
new file mode 100644
index 00000000000..13a63ad9599
--- /dev/null
+++ b/test/e2e/markdown.js
@@ -0,0 +1,49 @@
+casper.test.begin('markdown', 5, function (test) {
+  casper
+  .start('examples/markdown/index.html')
+  .then(function () {
+    test.assertEval(function () {
+      return document.querySelector('textarea').value === '# hello'
+    })
+    test.assertEval(function () {
+      return document.querySelector('#editor div')
+        .innerHTML === '<h1 id="hello">hello</h1>\n'
+    })
+  })
+  .then(function () {
+    this.sendKeys(
+      'textarea',
+      '## foo\n\n' +
+      '- bar\n' +
+      '- baz\n\n',
+      { keepFocus: true }
+    )
+    // keyUp(13)
+  })
+  .then(function () {
+    // assert the output is not updated yet because of
+    // debounce
+    test.assertEval(function () {
+      return document.querySelector('#editor div')
+        .innerHTML === '<h1 id="hello">hello</h1>\n'
+    })
+  })
+  .wait(300) // wait for debounce
+  .then(function () {
+    test.assertEval(function () {
+      return document.querySelector('textarea').value ===
+        '## foo\n\n- bar\n- baz\n\n# hello'
+    })
+    test.assertEval(function () {
+      return document.querySelector('#editor div')
+        .innerHTML ===
+          '<h2 id="foo">foo</h2>\n' +
+          '<ul>\n<li>bar</li>\n<li>baz</li>\n</ul>\n' +
+          '<h1 id="hello">hello</h1>\n'
+    })
+  })
+  // run
+  .run(function () {
+    test.done()
+  })
+})
diff --git a/test/e2e/markdown.spec.ts b/test/e2e/markdown.spec.ts
deleted file mode 100644
index 67b7244afae..00000000000
--- a/test/e2e/markdown.spec.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import {
-  setupPuppeteer,
-  expectByPolling,
-  getExampleUrl,
-  E2E_TIMEOUT
-} from './e2eUtils'
-
-describe('e2e: markdown', () => {
-  const { page, isVisible, value, html } = setupPuppeteer()
-
-  async function testMarkdown(apiType: 'classic' | 'composition') {
-    await page().goto(getExampleUrl('markdown', apiType))
-    expect(await isVisible('#editor')).toBe(true)
-    expect(await value('textarea')).toBe('# hello')
-    expect(await html('#editor div')).toBe('<h1 id="hello">hello</h1>\n')
-
-    await page().type('textarea', '\n## foo\n\n- bar\n- baz')
-
-    // assert the output is not updated yet because of debounce
-    // debounce has become unstable on CI so this assertion is disabled
-    // expect(await html('#editor div')).toBe('<h1 id="hello">hello</h1>\n')
-
-    await expectByPolling(
-      () => html('#editor div'),
-      '<h1 id="hello">hello</h1>\n' +
-        '<h2 id="foo">foo</h2>\n' +
-        '<ul>\n<li>bar</li>\n<li>baz</li>\n</ul>\n'
-    )
-  }
-
-  test(
-    'classic',
-    async () => {
-      await testMarkdown('classic')
-    },
-    E2E_TIMEOUT
-  )
-
-  test(
-    'composition',
-    async () => {
-      await testMarkdown('composition')
-    },
-    E2E_TIMEOUT
-  )
-})
diff --git a/test/e2e/modal.js b/test/e2e/modal.js
new file mode 100644
index 00000000000..9db8b525e1c
--- /dev/null
+++ b/test/e2e/modal.js
@@ -0,0 +1,22 @@
+casper.test.begin('modal', 8, function (test) {
+  casper
+  .start('examples/modal/index.html')
+  .then(function () {
+    test.assertNotVisible('.modal-mask')
+  })
+  .thenClick('#show-modal', function () {
+    test.assertVisible('.modal-mask')
+    test.assertVisible('.modal-wrapper')
+    test.assertVisible('.modal-container')
+    test.assertSelectorHasText('.modal-header h3', 'custom header')
+    test.assertSelectorHasText('.modal-body', 'default body')
+    test.assertSelectorHasText('.modal-footer', 'default footer')
+  })
+  .thenClick('.modal-default-button', function () {
+    test.assertNotVisible('.modal-mask')
+  })
+  // run
+  .run(function () {
+    test.done()
+  })
+})
diff --git a/test/e2e/svg.js b/test/e2e/svg.js
new file mode 100644
index 00000000000..ae3236e7e0b
--- /dev/null
+++ b/test/e2e/svg.js
@@ -0,0 +1,57 @@
+/* global stats, valueToPoint */
+
+casper.test.begin('svg', 18, function (test) {
+  casper
+  .start('examples/svg/index.html')
+  .then(function () {
+    test.assertElementCount('g', 1)
+    test.assertElementCount('polygon', 1)
+    test.assertElementCount('circle', 1)
+    test.assertElementCount('text', 6)
+    test.assertElementCount('label', 6)
+    test.assertElementCount('button', 7)
+    test.assertElementCount('input[type="range"]', 6)
+    test.assertEval(function () {
+      var points = stats.map(function (stat, i) {
+        var point = valueToPoint(stat.value, i, 6)
+        return point.x + ',' + point.y
+      }).join(' ')
+      return document.querySelector('polygon').attributes[0].value === points
+    })
+  })
+  .thenClick('button', function () {
+    test.assertElementCount('text', 5)
+    test.assertElementCount('label', 5)
+    test.assertElementCount('button', 6)
+    test.assertElementCount('input[type="range"]', 5)
+    test.assertEval(function () {
+      var points = stats.map(function (stat, i) {
+        var point = valueToPoint(stat.value, i, 5)
+        return point.x + ',' + point.y
+      }).join(' ')
+      return document.querySelector('polygon').attributes[0].value === points
+    })
+  })
+  .then(function () {
+    this.fill('#add', {
+      newlabel: 'foo'
+    })
+  })
+  .thenClick('#add > button', function () {
+    test.assertElementCount('text', 6)
+    test.assertElementCount('label', 6)
+    test.assertElementCount('button', 7)
+    test.assertElementCount('input[type="range"]', 6)
+    test.assertEval(function () {
+      var points = stats.map(function (stat, i) {
+        var point = valueToPoint(stat.value, i, 6)
+        return point.x + ',' + point.y
+      }).join(' ')
+      return document.querySelector('polygon').attributes[0].value === points
+    })
+  })
+  // run
+  .run(function () {
+    test.done()
+  })
+})
diff --git a/test/e2e/svg.spec.ts b/test/e2e/svg.spec.ts
deleted file mode 100644
index 5032ca4c664..00000000000
--- a/test/e2e/svg.spec.ts
+++ /dev/null
@@ -1,151 +0,0 @@
-import { setupPuppeteer, getExampleUrl, E2E_TIMEOUT } from './e2eUtils'
-
-declare const globalStats: {
-  label: string
-  value: number
-}[]
-
-declare function valueToPoint(
-  value: number,
-  index: number,
-  total: number
-): {
-  x: number
-  y: number
-}
-
-describe('e2e: svg', () => {
-  const { page, click, count, setValue, typeValue } = setupPuppeteer()
-
-  // assert the shape of the polygon is correct
-  async function assertPolygon(total: number) {
-    expect(
-      await page().evaluate(
-        total => {
-          const points = globalStats
-            .map((stat, i) => {
-              const point = valueToPoint(stat.value, i, total)
-              return point.x + ',' + point.y
-            })
-            .join(' ')
-          return (
-            document.querySelector('polygon')!.attributes[0].value === points
-          )
-        },
-        [total]
-      )
-    ).toBe(true)
-  }
-
-  // assert the position of each label is correct
-  async function assertLabels(total: number) {
-    const positions = await page().evaluate(
-      total => {
-        return globalStats.map((stat, i) => {
-          const point = valueToPoint(+stat.value + 10, i, total)
-          return [point.x, point.y]
-        })
-      },
-      [total]
-    )
-    for (let i = 0; i < total; i++) {
-      const textPosition = await page().$eval(
-        `text:nth-child(${i + 3})`,
-        node => [+node.attributes[0].value, +node.attributes[1].value]
-      )
-      expect(textPosition).toEqual(positions[i])
-    }
-  }
-
-  // assert each value of stats is correct
-  async function assertStats(expected: number[]) {
-    const statsValue = await page().evaluate(() => {
-      return globalStats.map(stat => +stat.value)
-    })
-    expect(statsValue).toEqual(expected)
-  }
-
-  function nthRange(n: number) {
-    return `#demo div:nth-child(${n + 1}) input[type="range"]`
-  }
-
-  async function testSvg(apiType: 'classic' | 'composition') {
-    await page().goto(getExampleUrl('svg', apiType))
-    await page().waitForSelector('svg')
-    expect(await count('g')).toBe(1)
-    expect(await count('polygon')).toBe(1)
-    expect(await count('circle')).toBe(1)
-    expect(await count('text')).toBe(6)
-    expect(await count('label')).toBe(6)
-    expect(await count('button')).toBe(7)
-    expect(await count('input[type="range"]')).toBe(6)
-    await assertPolygon(6)
-    await assertLabels(6)
-    await assertStats([100, 100, 100, 100, 100, 100])
-
-    await setValue(nthRange(1), '10')
-    await assertPolygon(6)
-    await assertLabels(6)
-    await assertStats([10, 100, 100, 100, 100, 100])
-
-    await click('button.remove')
-    expect(await count('text')).toBe(5)
-    expect(await count('label')).toBe(5)
-    expect(await count('button')).toBe(6)
-    expect(await count('input[type="range"]')).toBe(5)
-    await assertPolygon(5)
-    await assertLabels(5)
-    await assertStats([100, 100, 100, 100, 100])
-
-    await typeValue('input[name="newlabel"]', 'foo')
-    await click('#add > button')
-    expect(await count('text')).toBe(6)
-    expect(await count('label')).toBe(6)
-    expect(await count('button')).toBe(7)
-    expect(await count('input[type="range"]')).toBe(6)
-    await assertPolygon(6)
-    await assertLabels(6)
-    await assertStats([100, 100, 100, 100, 100, 100])
-
-    await setValue(nthRange(1), '10')
-    await assertPolygon(6)
-    await assertLabels(6)
-    await assertStats([10, 100, 100, 100, 100, 100])
-
-    await setValue(nthRange(2), '20')
-    await assertPolygon(6)
-    await assertLabels(6)
-    await assertStats([10, 20, 100, 100, 100, 100])
-
-    await setValue(nthRange(6), '60')
-    await assertPolygon(6)
-    await assertLabels(6)
-    await assertStats([10, 20, 100, 100, 100, 60])
-
-    await click('button.remove')
-    await assertPolygon(5)
-    await assertLabels(5)
-    await assertStats([20, 100, 100, 100, 60])
-
-    await setValue(nthRange(1), '10')
-    await assertPolygon(5)
-    await assertLabels(5)
-    await assertStats([10, 100, 100, 100, 60])
-  }
-
-  test(
-    'classic',
-    async () => {
-      await testSvg('classic')
-    },
-    E2E_TIMEOUT
-  )
-
-  test(
-    'composition',
-    async () => {
-      await testSvg('composition')
-    },
-    E2E_TIMEOUT
-  )
-})
diff --git a/test/e2e/todomvc.js b/test/e2e/todomvc.js
new file mode 100644
index 00000000000..a69d9274814
--- /dev/null
+++ b/test/e2e/todomvc.js
@@ -0,0 +1,295 @@
+/* global __utils__ */
+
+casper.test.begin('todomvc', 63, function (test) {
+  casper
+  .start('examples/todomvc/index.html')
+  .then(function () {
+    this.viewport(1000, 1000) // for appearing destroy buttons by mouse hover
+    test.assertNotVisible('.main', '.main should be hidden')
+    test.assertNotVisible('.footer', '.footer should be hidden')
+    test.assertElementCount('.filters .selected', 1, 'should have one filter selected')
+    test.assertSelectorHasText('.filters .selected', 'All', 'default filter should be "All"')
+  })
+
+  // let's add a new item -----------------------------------------------
+
+  .then(function () {
+    casper.sendKeys('.new-todo', 'test')
+  })
+  .then(function () {
+    // wait before hitting enter
+    // so v-model unlocks
+    createNewItem()
+  })
+  .then(function () {
+    test.assertElementCount('.todo', 1, 'new item should be created')
+    test.assertNotVisible('.todo .edit', 'new item edit box should be hidden')
+    test.assertSelectorHasText('.todo label', 'test', 'new item should have correct label text')
+    test.assertSelectorHasText('.todo-count strong', '1', 'remaining count should be 1')
+    test.assertEvalEquals(function () {
+      return __utils__.findOne('.todo .toggle').checked
+    }, false, 'new item toggle should not be checked')
+    test.assertVisible('.main', '.main should now be visible')
+    test.assertVisible('.footer', '.footer should now be visible')
+    test.assertNotVisible('.clear-completed', '.clear-completed should be hidden')
+    test.assertField({type: 'css', path: '.new-todo'}, '', 'new todo input should be reset')
+  })
+
+  // add another item ---------------------------------------------------
+
+  .then(function () {
+    createNewItem('test2')
+  })
+  .then(function () {
+    test.assertElementCount('.todo', 2, 'should have 2 items now')
+    test.assertSelectorHasText('.todo:nth-child(2) label', 'test2', 'new item should have correct label text')
+    test.assertSelectorHasText('.todo-count strong', '2', 'remaining count should be 2')
+  })
+
+  // mark one item as completed -----------------------------------------
+
+  .thenClick('.todo .toggle', function () {
+    test.assertElementCount('.todo.completed', 1, 'should have 1 item completed')
+    test.assertEval(function () {
+      return __utils__.findOne('.todo').classList.contains('completed')
+    }, 'it should be the first one')
+    test.assertSelectorHasText('.todo-count strong', '1', 'remaining count should be 1')
+    test.assertVisible('.clear-completed', '.clear-completed should now be visible')
+  })
+
+  // add yet another item -----------------------------------------------
+
+  .then(function () {
+    createNewItem('test3')
+  })
+  .then(function () {
+    test.assertElementCount('.todo', 3, 'should have 3 items now')
+    test.assertSelectorHasText('.todo:nth-child(3) label', 'test3', 'new item should have correct label text')
+    test.assertSelectorHasText('.todo-count strong', '2', 'remaining count should be 2')
+  })
+
+  // add moreeee, now we assume they all work properly ------------------
+
+  .then(function () {
+    createNewItem('test4')
+    createNewItem('test5')
+  })
+  .then(function () {
+    test.assertElementCount('.todo', 5, 'should have 5 items now')
+    test.assertSelectorHasText('.todo-count strong', '4', 'remaining count should be 4')
+  })
+
+  // check more as completed --------------------------------------------
+  .then(function () {
+    this.click('.todo:nth-child(4) .toggle')
+    this.click('.todo:nth-child(5) .toggle')
+  })
+  .then(function () {
+    test.assertElementCount('.todo.completed', 3, 'should have 3 item completed')
+    test.assertSelectorHasText('.todo-count strong', '2', 'remaining count should be 2')
+  })
+
+  // remove a completed item --------------------------------------------
+
+  .then(function () {
+    this.mouse.move('.todo:nth-child(1)')
+  })
+  .thenClick('.todo:nth-child(1) .destroy', function () {
+    test.assertElementCount('.todo', 4, 'should have 4 items now')
+    test.assertElementCount('.todo.completed', 2, 'should have 2 item completed')
+    test.assertSelectorHasText('.todo-count strong', '2', 'remaining count should be 2')
+  })
+
+  // remove a incompleted item ------------------------------------------
+
+  .then(function () {
+    this.mouse.move('.todo:nth-child(2)')
+  })
+  .thenClick('.todo:nth-child(2) .destroy', function () {
+    test.assertElementCount('.todo', 3, 'should have 3 items now')
+    test.assertElementCount('.todo.completed', 2, 'should have 2 item completed')
+    test.assertSelectorHasText('.todo-count strong', '1', 'remaining count should be 1')
+  })
+
+  // remove all completed ------------------------------------------------
+
+  .thenClick('.clear-completed', function () {
+    test.assertElementCount('.todo', 1, 'should have 1 item now')
+    test.assertSelectorHasText('.todo label', 'test2', 'the remaining one should be the second one')
+    test.assertElementCount('.todo.completed', 0, 'should have no completed items now')
+    test.assertSelectorHasText('.todo-count strong', '1', 'remaining count should be 1')
+    test.assertNotVisible('.clear-completed', '.clear-completed should be hidden')
+  })
+
+  // prepare to test filters ------------------------------------------------
+  .then(function () {
+    createNewItem('test')
+    createNewItem('test')
+  })
+  .then(function () {
+    this.click('.todo:nth-child(2) .toggle')
+    this.click('.todo:nth-child(3) .toggle')
+  })
+
+  // active filter ----------------------------------------------------------
+  .thenClick('.filters li:nth-child(2) a', function () {
+    test.assertElementCount('.todo', 1, 'filter active should have 1 item')
+    test.assertElementCount('.todo.completed', 0, 'visible items should be incomplete')
+  })
+
+  // add item with filter active --------------------------------------------
+  // mostly make sure v-repeat works well with v-if
+  .then(function () {
+    createNewItem('test')
+  })
+  .then(function () {
+    test.assertElementCount('.todo', 2, 'should be able to create new item when fitler active')
+  })
+
+  // completed filter -------------------------------------------------------
+  .thenClick('.filters li:nth-child(3) a', function () {
+    test.assertElementCount('.todo', 2, 'filter completed should have 2 items')
+    test.assertElementCount('.todo.completed', 2, 'visible items should be completed')
+  })
+
+  // active filter on page load ---------------------------------------------
+  .thenOpen('examples/todomvc/index.html#/active', function () {
+    test.assertElementCount('.todo', 2, 'filter active should have 2 items')
+    test.assertElementCount('.todo.completed', 0, 'visible items should be incompleted')
+    test.assertSelectorHasText('.todo-count strong', '2', 'remaining count should be 2')
+  })
+
+  // completed filter on page load ------------------------------------------
+  .thenOpen('examples/todomvc/index.html#/completed', function () {
+    test.assertElementCount('.todo', 2, 'filter completed should have 2 items')
+    test.assertElementCount('.todo.completed', 2, 'visible items should be completed')
+    test.assertSelectorHasText('.todo-count strong', '2', 'remaining count should be 2')
+  })
+
+  // toggling todos when filter is active -----------------------------------
+  .thenClick('.todo .toggle', function () {
+    test.assertElementCount('.todo', 1, 'should have only 1 item left')
+  })
+  .thenClick('.filters li:nth-child(2) a', function () {
+    test.assertElementCount('.todo', 3, 'should have only 3 items now')
+  })
+  .thenClick('.todo .toggle', function () {
+    test.assertElementCount('.todo', 2, 'should have only 2 items now')
+  })
+
+  // test editing triggered by blur ------------------------------------------
+  .thenClick('.filters li:nth-child(1) a')
+  .then(function () {
+    doubleClick('.todo:nth-child(1) label')
+  })
+  .then(function () {
+    test.assertElementCount('.todo.editing', 1, 'should have one item being edited')
+    test.assertEval(function () {
+      var input = document.querySelector('.todo:nth-child(1) .edit')
+      return input === document.activeElement
+    }, 'edit input should be focused')
+  })
+  .then(function () {
+    resetField()
+    this.sendKeys('.todo:nth-child(1) .edit', 'edited!') // doneEdit triggered by blur
+  })
+  .then(function () {
+    test.assertElementCount('.todo.editing', 0, 'item should no longer be edited')
+    test.assertSelectorHasText('.todo:nth-child(1) label', 'edited!', 'item should have updated text')
+  })
+
+  // test editing triggered by enter ----------------------------------------
+  .then(function () {
+    doubleClick('.todo label')
+  })
+  .then(function () {
+    resetField()
+    this.sendKeys('.todo:nth-child(1) .edit', 'edited again!', { keepFocus: true })
+    keyUp(13) // Enter
+  })
+  .then(function () {
+    test.assertElementCount('.todo.editing', 0, 'item should no longer be edited')
+    test.assertSelectorHasText('.todo:nth-child(1) label', 'edited again!', 'item should have updated text')
+  })
+
+  // test cancel ------------------------------------------------------------
+  .then(function () {
+    doubleClick('.todo label')
+  })
+  .then(function () {
+    resetField()
+    this.sendKeys('.todo:nth-child(1) .edit', 'cancel test', { keepFocus: true })
+    keyUp(27) // ESC
+  })
+  .then(function () {
+    test.assertElementCount('.todo.editing', 0, 'item should no longer be edited')
+    test.assertSelectorHasText('.todo label', 'edited again!', 'item should not have updated text')
+  })
+
+  // test empty input remove ------------------------------------------------
+  .then(function () {
+    doubleClick('.todo label')
+  })
+  .then(function () {
+    resetField()
+    this.sendKeys('.todo:nth-child(1) .edit', ' ')
+  })
+  .then(function () {
+    test.assertElementCount('.todo', 3, 'item should have been deleted')
+  })
+
+  // test toggle all
+  .thenClick('.toggle-all', function () {
+    test.assertElementCount('.todo.completed', 3, 'should toggle all items to completed')
+  })
+  .thenClick('.toggle-all', function () {
+    test.assertElementCount('.todo:not(.completed)', 3, 'should toggle all items to active')
+  })
+
+  // run
+  .run(function () {
+    test.done()
+  })
+
+  // helper ===============
+
+  function createNewItem (text) {
+    if (text) {
+      casper.sendKeys('.new-todo', text)
+    }
+    casper.evaluate(function () {
+      // casper.mouseEvent can't set keyCode
+      var field = document.querySelector('.new-todo')
+      var e = document.createEvent('HTMLEvents')
+      e.initEvent('keyup', true, true)
+      e.keyCode = 13
+      field.dispatchEvent(e)
+    })
+  }
+
+  function doubleClick (selector) {
+    casper.evaluate(function (selector) {
+      var el = document.querySelector(selector)
+      var e = document.createEvent('MouseEvents')
+      e.initMouseEvent('dblclick', true, true, null, 1, 0, 0, 0, 0, false, false, false, false, 0, null)
+      el.dispatchEvent(e)
+    }, selector)
+  }
+
+  function keyUp (code) {
+    casper.evaluate(function (code) {
+      var input = document.querySelector('.todo:nth-child(1) .edit')
+      var e = document.createEvent('HTMLEvents')
+      e.initEvent('keyup', true, true)
+      e.keyCode = code
+      input.dispatchEvent(e)
+    }, code)
+  }
+
+  function resetField () {
+    // somehow casper.sendKey() option reset:true doesn't work
+    casper.evaluate(function () {
+      document.querySelector('.todo:nth-child(1) .edit').value = ''
+    })
+  }
+})
diff --git a/test/e2e/todomvc.spec.ts b/test/e2e/todomvc.spec.ts
deleted file mode 100644
index f1de97e6354..00000000000
--- a/test/e2e/todomvc.spec.ts
+++ /dev/null
@@ -1,182 +0,0 @@
-import { setupPuppeteer, getExampleUrl, E2E_TIMEOUT } from './e2eUtils'
-
-describe('e2e: todomvc', () => {
-  const {
-    page,
-    click,
-    isVisible,
-    count,
-    text,
-    value,
-    isChecked,
-    isFocused,
-    classList,
-    enterValue,
-    clearValue
-  } = setupPuppeteer()
-
-  async function removeItemAt(n: number) {
-    const item = (await page().$('.todo:nth-child(' + n + ')'))!
-    const itemBBox = (await item.boundingBox())!
-    await page().mouse.move(itemBBox.x + 10, itemBBox.y + 10)
-    await click('.todo:nth-child(' + n + ') .destroy')
-  }
-
-  async function testTodomvc(apiType: 'classic' | 'composition') {
-    const baseUrl = getExampleUrl('todomvc', apiType)
-    await page().goto(baseUrl)
-    expect(await isVisible('.main')).toBe(false)
-    expect(await isVisible('.footer')).toBe(false)
-    expect(await count('.filters .selected')).toBe(1)
-    expect(await text('.filters .selected')).toBe('All')
-    expect(await count('.todo')).toBe(0)
-
-    await enterValue('.new-todo', 'test')
-    expect(await count('.todo')).toBe(1)
-    expect(await isVisible('.todo .edit')).toBe(false)
-    expect(await text('.todo label')).toBe('test')
-    expect(await text('.todo-count strong')).toBe('1')
-    expect(await isChecked('.todo .toggle')).toBe(false)
-    expect(await isVisible('.main')).toBe(true)
-    expect(await isVisible('.footer')).toBe(true)
-    expect(await isVisible('.clear-completed')).toBe(false)
-    expect(await value('.new-todo')).toBe('')
-
-    await enterValue('.new-todo', 'test2')
-    expect(await count('.todo')).toBe(2)
-    expect(await text('.todo:nth-child(2) label')).toBe('test2')
-    expect(await text('.todo-count strong')).toBe('2')
-
-    // toggle
-    await click('.todo .toggle')
-    expect(await count('.todo.completed')).toBe(1)
-    expect(await classList('.todo:nth-child(1)')).toContain('completed')
-    expect(await text('.todo-count strong')).toBe('1')
-    expect(await isVisible('.clear-completed')).toBe(true)
-
-    await enterValue('.new-todo', 'test3')
-    expect(await count('.todo')).toBe(3)
-    expect(await text('.todo:nth-child(3) label')).toBe('test3')
-    expect(await text('.todo-count strong')).toBe('2')
-
-    await enterValue('.new-todo', 'test4')
-    await enterValue('.new-todo', 'test5')
-    expect(await count('.todo')).toBe(5)
-    expect(await text('.todo-count strong')).toBe('4')
-
-    // toggle more
-    await click('.todo:nth-child(4) .toggle')
-    await click('.todo:nth-child(5) .toggle')
-    expect(await count('.todo.completed')).toBe(3)
-    expect(await text('.todo-count strong')).toBe('2')
-
-    // remove
-    await removeItemAt(1)
-    expect(await count('.todo')).toBe(4)
-    expect(await count('.todo.completed')).toBe(2)
-    expect(await text('.todo-count strong')).toBe('2')
-    await removeItemAt(2)
-    expect(await count('.todo')).toBe(3)
-    expect(await count('.todo.completed')).toBe(2)
-    expect(await text('.todo-count strong')).toBe('1')
-
-    // remove all
-    await click('.clear-completed')
-    expect(await count('.todo')).toBe(1)
-    expect(await text('.todo label')).toBe('test2')
-    expect(await count('.todo.completed')).toBe(0)
-    expect(await text('.todo-count strong')).toBe('1')
-    expect(await isVisible('.clear-completed')).toBe(false)
-
-    // prepare to test filters
-    await enterValue('.new-todo', 'test')
-    await enterValue('.new-todo', 'test')
-    await click('.todo:nth-child(2) .toggle')
-    await click('.todo:nth-child(3) .toggle')
-
-    // active filter
-    await click('.filters li:nth-child(2) a')
-    expect(await count('.todo')).toBe(1)
-    expect(await count('.todo.completed')).toBe(0)
-    // add item with filter active
-    await enterValue('.new-todo', 'test')
-    expect(await count('.todo')).toBe(2)
-
-    // completed filter
-    await click('.filters li:nth-child(3) a')
-    expect(await count('.todo')).toBe(2)
-    expect(await count('.todo.completed')).toBe(2)
-
-    // filter on page load
-    await page().goto(`${baseUrl}#active`)
-    expect(await count('.todo')).toBe(2)
-    expect(await count('.todo.completed')).toBe(0)
-    expect(await text('.todo-count strong')).toBe('2')
-
-    // completed on page load
-    await page().goto(`${baseUrl}#completed`)
-    expect(await count('.todo')).toBe(2)
-    expect(await count('.todo.completed')).toBe(2)
-    expect(await text('.todo-count strong')).toBe('2')
-
-    // toggling with filter active
-    await click('.todo .toggle')
-    expect(await count('.todo')).toBe(1)
-    await click('.filters li:nth-child(2) a')
-    expect(await count('.todo')).toBe(3)
-    await click('.todo .toggle')
-    expect(await count('.todo')).toBe(2)
-
-    // editing triggered by blur
-    await click('.filters li:nth-child(1) a')
-    await click('.todo:nth-child(1) label', { clickCount: 2 })
-    expect(await count('.todo.editing')).toBe(1)
-    expect(await isFocused('.todo:nth-child(1) .edit')).toBe(true)
-    await clearValue('.todo:nth-child(1) .edit')
-    await page().type('.todo:nth-child(1) .edit', 'edited!')
-    await click('.new-todo') // blur
-    expect(await count('.todo.editing')).toBe(0)
-    expect(await text('.todo:nth-child(1) label')).toBe('edited!')
-
-    // editing triggered by enter
-    await click('.todo label', { clickCount: 2 })
-    await enterValue('.todo:nth-child(1) .edit', 'edited again!')
-    expect(await count('.todo.editing')).toBe(0)
-    expect(await text('.todo:nth-child(1) label')).toBe('edited again!')
-
-    // cancel
-    await click('.todo label', { clickCount: 2 })
-    await clearValue('.todo:nth-child(1) .edit')
-    await page().type('.todo:nth-child(1) .edit', 'edited!')
-    await page().keyboard.press('Escape')
-    expect(await count('.todo.editing')).toBe(0)
-    expect(await text('.todo:nth-child(1) label')).toBe('edited again!')
-
-    // empty value should remove
-    await click('.todo label', { clickCount: 2 })
-    await enterValue('.todo:nth-child(1) .edit', ' ')
-    expect(await count('.todo')).toBe(3)
-
-    // toggle all
-    await click('.toggle-all+label')
-    expect(await count('.todo.completed')).toBe(3)
-    await click('.toggle-all+label')
-    expect(await count('.todo:not(.completed)')).toBe(3)
-  }
-
-  test(
-    'classic',
-    async () => {
-      await testTodomvc('classic')
-    },
-    E2E_TIMEOUT
-  )
-
-  test(
-    'composition',
-    async () => {
-      await testTodomvc('composition')
-    },
-    E2E_TIMEOUT
-  )
-})
diff --git a/test/e2e/tree.js b/test/e2e/tree.js
new file mode 100644
index 00000000000..fd0b87a5b0c
--- /dev/null
+++ b/test/e2e/tree.js
@@ -0,0 +1,53 @@
+casper.test.begin('tree', 22, function (test) {
+  casper
+  .start('examples/tree/index.html')
+  .then(function () {
+    test.assertElementCount('.item', 12)
+    test.assertElementCount('.item > ul', 4)
+    test.assertNotVisible('#demo li ul')
+    test.assertSelectorHasText('#demo li div span', '[+]')
+  })
+  .thenClick('.bold', function () {
+    test.assertVisible('#demo ul')
+    test.assertSelectorHasText('#demo li div span', '[-]')
+    test.assertSelectorHasText('#demo ul > .item:nth-child(1)', 'hello')
+    test.assertSelectorHasText('#demo ul > .item:nth-child(2)', 'wat')
+    test.assertSelectorHasText('#demo ul > .item:nth-child(3)', 'child folder')
+    test.assertSelectorHasText('#demo ul > .item:nth-child(3)', '[+]')
+    test.assertEval(function () {
+      return document.querySelector('#demo li ul').children.length === 4
+    })
+  })
+  .thenClick('#demo ul .bold', function () {
+    test.assertVisible('#demo ul ul')
+    test.assertSelectorHasText('#demo ul > .item:nth-child(3)', '[-]')
+    test.assertEval(function () {
+      return document.querySelector('#demo ul ul').children.length === 5
+    })
+  })
+  .thenClick('.bold', function () {
+    test.assertNotVisible('#demo ul')
+    test.assertSelectorHasText('#demo li div span', '[+]')
+  })
+  .thenClick('.bold', function () {
+    test.assertVisible('#demo ul')
+    test.assertSelectorHasText('#demo li div span', '[-]')
+  })
+  .then(function () {
+    casper.mouseEvent('dblclick', '#demo ul > .item div')
+  })
+  .then(function () {
+    test.assertElementCount('.item', 13)
+    test.assertElementCount('.item > ul', 5)
+    test.assertSelectorHasText('#demo ul > .item:nth-child(1)', '[-]')
+    test.assertEval(function () {
+      var firstItem = document.querySelector('#demo ul > .item:nth-child(1)')
+      var ul = firstItem.querySelector('ul')
+      return ul.children.length === 2
+    })
+  })
+  // run
+  .run(function () {
+    test.done()
+  })
+})
diff --git a/test/e2e/tree.spec.ts b/test/e2e/tree.spec.ts
deleted file mode 100644
index 970aaa5eaf2..00000000000
--- a/test/e2e/tree.spec.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-import { setupPuppeteer, getExampleUrl, E2E_TIMEOUT } from './e2eUtils'
-
-describe('e2e: tree', () => {
-  const { page, click, count, text, childrenCount, isVisible } =
-    setupPuppeteer()
-
-  async function testTree(apiType: 'classic' | 'composition') {
-    await page().goto(getExampleUrl('tree', apiType))
-    expect(await count('.item')).toBe(12)
-    expect(await count('.add')).toBe(4)
-    expect(await count('.item > ul')).toBe(4)
-    expect(await isVisible('#demo li ul')).toBe(false)
-    expect(await text('#demo li div span')).toBe('[+]')
-
-    // expand root
-    await click('.bold')
-    expect(await isVisible('#demo ul')).toBe(true)
-    expect(await childrenCount('#demo li ul')).toBe(4)
-    expect(await text('#demo li div span')).toContain('[-]')
-    expect(await text('#demo > .item > ul > .item:nth-child(1)')).toContain(
-      'hello'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(2)')).toContain(
-      'wat'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(3)')).toContain(
-      'child folder'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(3)')).toContain(
-      '[+]'
-    )
-
-    // add items to root
-    await click('#demo > .item > ul > .add')
-    expect(await childrenCount('#demo li ul')).toBe(5)
-    expect(await text('#demo > .item > ul > .item:nth-child(1)')).toContain(
-      'hello'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(2)')).toContain(
-      'wat'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(3)')).toContain(
-      'child folder'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(3)')).toContain(
-      '[+]'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(4)')).toContain(
-      'new stuff'
-    )
-
-    // add another item
-    await click('#demo > .item > ul > .add')
-    expect(await childrenCount('#demo li ul')).toBe(6)
-    expect(await text('#demo > .item > ul > .item:nth-child(1)')).toContain(
-      'hello'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(2)')).toContain(
-      'wat'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(3)')).toContain(
-      'child folder'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(3)')).toContain(
-      '[+]'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(4)')).toContain(
-      'new stuff'
-    )
-    expect(await text('#demo > .item > ul > .item:nth-child(5)')).toContain(
-      'new stuff'
-    )
-
-    await click('#demo ul .bold')
-    expect(await isVisible('#demo ul ul')).toBe(true)
-    expect(await text('#demo ul > .item:nth-child(3)')).toContain('[-]')
-    expect(await childrenCount('#demo ul ul')).toBe(5)
-
-    await click('.bold')
-    expect(await isVisible('#demo ul')).toBe(false)
-    expect(await text('#demo li div span')).toContain('[+]')
-    await click('.bold')
-    expect(await isVisible('#demo ul')).toBe(true)
-    expect(await text('#demo li div span')).toContain('[-]')
-
-    await click('#demo ul > .item div', { clickCount: 2 })
-    expect(await count('.item')).toBe(15)
-    expect(await count('.item > ul')).toBe(5)
-    expect(await text('#demo ul > .item:nth-child(1)')).toContain('[-]')
-    expect(await childrenCount('#demo ul > .item:nth-child(1) > ul')).toBe(2)
-  }
-
-  test(
-    'classic',
-    async () => {
-      await testTree('classic')
-    },
-    E2E_TIMEOUT
-  )
-
-  test(
-    'composition',
-    async () => {
-      await testTree('composition')
-    },
-    E2E_TIMEOUT
-  )
-})
diff --git a/test/helpers/classlist.ts b/test/helpers/classlist.ts
deleted file mode 100644
index 2e617e6b255..00000000000
--- a/test/helpers/classlist.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-expect.extend({
-  toHaveClass(el: Element, cls: string) {
-    const pass = el.classList
-      ? el.classList.contains(cls)
-      : (el.getAttribute('class') || '').split(/\s+/g).indexOf(cls) > -1
-    return {
-      pass,
-      message: () =>
-        `Expected element${pass ? ' ' : ' not '}to have class ${cls}`
-    }
-  }
-})
diff --git a/test/helpers/shim-done.ts b/test/helpers/shim-done.ts
deleted file mode 100644
index 902d46a31bb..00000000000
--- a/test/helpers/shim-done.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-// wrap tests to support test('foo', done => {...}) interface
-const _test = test
-
-const wait = (): [() => void, Promise<void>] => {
-  let done
-  const p = new Promise<void>((resolve, reject) => {
-    done = resolve
-    done.fail = reject
-  })
-  return [done, p]
-}
-
-const shimmed =
-  ((global as any).it =
-  (global as any).test =
-    (desc: string, fn?: any, timeout?: number) => {
-      if (fn && fn.length > 0) {
-        _test(
-          desc,
-          () => {
-            const [done, p] = wait()
-            fn(done)
-            return p
-          },
-          timeout
-        )
-      } else {
-        _test(desc, fn, timeout)
-      }
-    })
-
-;['skip', 'only', 'todo', 'concurrent'].forEach(key => {
-  shimmed[key] = _test[key]
-})
-
-export {}
diff --git a/test/helpers/test-object-option.ts b/test/helpers/test-object-option.ts
deleted file mode 100644
index 39be7ee8b29..00000000000
--- a/test/helpers/test-object-option.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import Vue from 'vue'
-
-export default function testObjectOption(name) {
-  it(`Options ${name}: should warn non object value`, () => {
-    const options = {}
-    options[name] = () => {}
-    new Vue(options)
-    expect(`Invalid value for option "${name}"`).toHaveBeenWarned()
-  })
-
-  it(`Options ${name}: should not warn valid object value`, () => {
-    const options = {}
-    options[name] = {}
-    new Vue(options)
-    expect(`Invalid value for option "${name}"`).not.toHaveBeenWarned()
-  })
-}
diff --git a/test/helpers/to-have-warned.ts b/test/helpers/to-have-warned.ts
deleted file mode 100644
index cba4bc0cafc..00000000000
--- a/test/helpers/to-have-warned.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-import { SpyInstance } from 'vitest'
-
-expect.extend({
-  toHaveBeenWarned(received: string) {
-    asserted.add(received)
-    const passed = warn.mock.calls.some(args =>
-      String(args[0]).includes(received)
-    )
-    if (passed) {
-      return {
-        pass: true,
-        message: () => `expected "${received}" not to have been warned.`
-      }
-    } else {
-      const msgs = warn.mock.calls.map(args => args[0]).join('\n - ')
-      return {
-        pass: false,
-        message: () =>
-          `expected "${received}" to have been warned` +
-          (msgs.length
-            ? `.\n\nActual messages:\n\n - ${msgs}`
-            : ` but no warning was recorded.`)
-      }
-    }
-  },
-
-  toHaveBeenWarnedLast(received: string) {
-    asserted.add(received)
-    const passed =
-      warn.mock.calls[warn.mock.calls.length - 1]?.[0].includes(received)
-    if (passed) {
-      return {
-        pass: true,
-        message: () => `expected "${received}" not to have been warned last.`
-      }
-    } else {
-      const msgs = warn.mock.calls.map(args => args[0]).join('\n - ')
-      return {
-        pass: false,
-        message: () =>
-          `expected "${received}" to have been warned last.\n\nActual messages:\n\n - ${msgs}`
-      }
-    }
-  },
-
-  toHaveBeenWarnedTimes(received: string, n: number) {
-    asserted.add(received)
-    let found = 0
-    warn.mock.calls.forEach(args => {
-      if (args[0].includes(received)) {
-        found++
-      }
-    })
-
-    if (found === n) {
-      return {
-        pass: true,
-        message: () => `expected "${received}" to have been warned ${n} times.`
-      }
-    } else {
-      return {
-        pass: false,
-        message: () =>
-          `expected "${received}" to have been warned ${n} times but got ${found}.`
-      }
-    }
-  },
-
-  toHaveBeenTipped(received: string) {
-    const passed = tip.mock.calls.some(args => args[0].includes(received))
-    if (passed) {
-      return {
-        pass: true,
-        message: () => `expected "${received}" not to have been tipped.`
-      }
-    } else {
-      const msgs = warn.mock.calls.map(args => args[0]).join('\n - ')
-      return {
-        pass: false,
-        message: () =>
-          `expected "${received}" to have been tipped` +
-          (msgs.length
-            ? `.\n\nActual messages:\n\n - ${msgs}`
-            : ` but no tip was recorded.`)
-      }
-    }
-  }
-})
-
-let warn: SpyInstance
-let tip: SpyInstance
-const asserted: Set<string> = new Set()
-
-beforeEach(() => {
-  asserted.clear()
-  warn = vi.spyOn(console, 'error')
-  tip = vi.spyOn(console, 'warn').mockImplementation(() => {})
-  warn.mockImplementation(() => {})
-})
-
-afterEach(() => {
-  const assertedArray = Array.from(asserted)
-  const nonAssertedWarnings = warn.mock.calls
-    .map(args => args[0])
-    .filter(received => {
-      return !assertedArray.some(assertedMsg => {
-        return String(received).includes(assertedMsg)
-      })
-    })
-  warn.mockRestore()
-  if (nonAssertedWarnings.length) {
-    throw new Error(
-      `test case threw unexpected warnings:\n - ${nonAssertedWarnings.join(
-        '\n - '
-      )}`
-    )
-  }
-})
diff --git a/test/helpers/trigger-event.ts b/test/helpers/trigger-event.ts
deleted file mode 100644
index cbe5e76531e..00000000000
--- a/test/helpers/trigger-event.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export function triggerEvent(target, event, process) {
-  const e = document.createEvent('HTMLEvents')
-  e.initEvent(event, true, true)
-  if (event === 'click') {
-    // @ts-expect-error Button is readonly
-    ;(e as MouseEvent).button = 0
-  }
-  if (process) process(e)
-  target.dispatchEvent(e)
-}
diff --git a/test/helpers/vdom.ts b/test/helpers/vdom.ts
deleted file mode 100644
index ab4af6e9883..00000000000
--- a/test/helpers/vdom.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import VNode from 'core/vdom/vnode'
-
-export function createTextVNode(text) {
-  return new VNode(undefined, undefined, undefined, text)
-}
diff --git a/test/helpers/wait-for-update.ts b/test/helpers/wait-for-update.ts
deleted file mode 100644
index 9adf35fcd63..00000000000
--- a/test/helpers/wait-for-update.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import Vue from 'vue'
-
-// helper for async assertions.
-// Use like this:
-//
-// vm.a = 123
-// waitForUpdate(() => {
-//   expect(vm.$el.textContent).toBe('123')
-//   vm.a = 234
-// })
-// .then(() => {
-//   // more assertions...
-// })
-// .then(done)
-
-interface Job extends Function {
-  wait?: boolean
-  fail?: (e: any) => void
-}
-
-const waitForUpdate = (initialCb: Job) => {
-  let end
-  const queue: Job[] = initialCb ? [initialCb] : []
-
-  function shift() {
-    const job = queue.shift()
-    if (queue.length) {
-      let hasError = false
-      try {
-        job!.wait ? job!(shift) : job!()
-      } catch (e) {
-        hasError = true
-        const done = queue[queue.length - 1]
-        if (done && done.fail) {
-          done.fail(e)
-        }
-      }
-      if (!hasError && !job!.wait) {
-        if (queue.length) {
-          Vue.nextTick(shift)
-        }
-      }
-    } else if (job && (job.fail || job === end)) {
-      job() // done
-    }
-  }
-
-  Vue.nextTick(() => {
-    if (!queue.length || (!end && !queue[queue.length - 1]!.fail)) {
-      throw new Error('waitForUpdate chain is missing .then(done)')
-    }
-    shift()
-  })
-
-  const chainer = {
-    then: nextCb => {
-      queue.push(nextCb)
-      return chainer
-    },
-    thenWaitFor: wait => {
-      if (typeof wait === 'number') {
-        wait = timeout(wait)
-      }
-      wait.wait = true
-      queue.push(wait)
-      return chainer
-    },
-    end: endFn => {
-      queue.push(endFn)
-      end = endFn
-    }
-  }
-
-  return chainer
-}
-
-function timeout(n) {
-  return next => setTimeout(next, n)
-}
-
-export { waitForUpdate }
diff --git a/test/test-env.d.ts b/test/test-env.d.ts
deleted file mode 100644
index be1fa6a7d85..00000000000
--- a/test/test-env.d.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-interface Chainer {
-  then(next: Function): this
-  thenWaitFor(n: number | Function): this
-  end(endFn: Function): void
-}
-
-declare function waitForUpdate(cb: Function): Chainer
-
-declare function createTextVNode(arg?: string): any
-
-declare function triggerEvent(
-  target: Element,
-  event: string,
-  process?: (e: any) => void
-): void
-
-// vitest extends jest namespace so we can just extend jest.Matchers
-declare namespace jest {
-  interface Matchers<R, T> {
-    toHaveBeenWarned(): R
-    toHaveBeenWarnedLast(): R
-    toHaveBeenWarnedTimes(n: number): R
-    toHaveBeenTipped(): R
-    toHaveClass(cls: string): R
-  }
-}
-
-declare const jasmine: {
-  createSpy: (id?: string) => {
-    (...args: any[]): any
-    calls: {
-      count(): number
-    }
-  }
-  addMatchers(matchers: any): void
-}
diff --git a/test/transition/helpers.ts b/test/transition/helpers.ts
deleted file mode 100644
index a47fd01cb55..00000000000
--- a/test/transition/helpers.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-export { waitForUpdate } from '../helpers/wait-for-update'
-export { nextFrame } from 'web/runtime/transition-util'
-
-// toHaveBeenWarned() matcher
-function noop() {}
-
-if (typeof console === 'undefined') {
-  // @ts-ignore
-  window.console = {
-    warn: noop,
-    error: noop
-  }
-}
-
-// avoid info messages during test
-console.info = noop
-
-let asserted
-
-function createCompareFn(spy) {
-  const hasWarned = msg => {
-    let count = spy.calls.count()
-    let args
-    while (count--) {
-      args = spy.calls.argsFor(count)
-      if (args.some(containsMsg)) {
-        return true
-      }
-    }
-
-    function containsMsg(arg) {
-      return arg.toString().indexOf(msg) > -1
-    }
-  }
-
-  return {
-    compare: msg => {
-      asserted = asserted.concat(msg)
-      const warned = Array.isArray(msg) ? msg.some(hasWarned) : hasWarned(msg)
-      return {
-        pass: warned,
-        message: warned
-          ? 'Expected message "' + msg + '" not to have been warned'
-          : 'Expected message "' + msg + '" to have been warned'
-      }
-    }
-  }
-}
-
-// define custom matcher for warnings
-beforeEach(() => {
-  asserted = []
-  // @ts-ignore
-  spyOn(console, 'warn')
-  // @ts-ignore
-  spyOn(console, 'error')
-  jasmine.addMatchers({
-    toHaveBeenWarned: () => createCompareFn(console.error),
-    toHaveBeenTipped: () => createCompareFn(console.warn)
-  })
-})
-
-afterEach(done => {
-  const warned = msg =>
-    asserted.some(assertedMsg => msg.toString().indexOf(assertedMsg) > -1)
-  // @ts-ignore
-  let count = console.error.calls.count()
-  let args
-  while (count--) {
-    // @ts-ignore
-    args = console.error.calls.argsFor(count)
-    if (!warned(args[0])) {
-      // @ts-ignore
-      done.fail(`Unexpected console.error message: ${args[0]}`)
-      return
-    }
-  }
-  done()
-})
-
-// injectStyles helper
-function insertCSS(text) {
-  const cssEl = document.createElement('style')
-  cssEl.textContent = text.trim()
-  document.head.appendChild(cssEl)
-}
-
-const duration = process.env.CI ? 200 : 50
-const buffer = process.env.CI ? 20 : 5
-let injected = false
-
-export function injectStyles() {
-  if (injected) return { duration, buffer }
-  injected = true
-  insertCSS(`
-    .test {
-      -webkit-transition: opacity ${duration}ms ease;
-      transition: opacity ${duration}ms ease;
-    }
-    .group-move {
-      -webkit-transition: -webkit-transform ${duration}ms ease;
-      transition: transform ${duration}ms ease;
-    }
-    .v-appear, .v-enter, .v-leave-active,
-    .test-appear, .test-enter, .test-leave-active,
-    .hello, .bye.active,
-    .changed-enter {
-      opacity: 0;
-    }
-    .test-anim-enter-active {
-      animation: test-enter ${duration}ms;
-      -webkit-animation: test-enter ${duration}ms;
-    }
-    .test-anim-leave-active {
-      animation: test-leave ${duration}ms;
-      -webkit-animation: test-leave ${duration}ms;
-    }
-    .test-anim-long-enter-active {
-      animation: test-enter ${duration * 2}ms;
-      -webkit-animation: test-enter ${duration * 2}ms;
-    }
-    .test-anim-long-leave-active {
-      animation: test-leave ${duration * 2}ms;
-      -webkit-animation: test-leave ${duration * 2}ms;
-    }
-    @keyframes test-enter {
-      from { opacity: 0 }
-      to { opacity: 1 }
-    }
-    @-webkit-keyframes test-enter {
-      from { opacity: 0 }
-      to { opacity: 1 }
-    }
-    @keyframes test-leave {
-      from { opacity: 1 }
-      to { opacity: 0 }
-    }
-    @-webkit-keyframes test-leave {
-      from { opacity: 1 }
-      to { opacity: 0 }
-    }
-  `)
-  return { duration, buffer }
-}
diff --git a/test/transition/karma.conf.js b/test/transition/karma.conf.js
deleted file mode 100644
index 02a439e3b3a..00000000000
--- a/test/transition/karma.conf.js
+++ /dev/null
@@ -1,28 +0,0 @@
-const featureFlags = require('../../scripts/feature-flags')
-process.env.CHROME_BIN = require('puppeteer').executablePath()
-
-const define = {
-  __DEV__: `true`,
-  'process.env.CI': String(!!process.env.CI)
-}
-
-for (const key in featureFlags) {
-  define[`process.env.${key}`] = String(featureFlags[key])
-}
-
-module.exports = function (config) {
-  config.set({
-    basePath: '.',
-    frameworks: ['jasmine'],
-    files: ['*.spec.ts'],
-    preprocessors: {
-      '*.spec.ts': ['esbuild']
-    },
-    esbuild: {
-      define
-    },
-    browsers: ['ChromeHeadless'],
-    plugins: ['karma-jasmine', 'karma-esbuild', 'karma-chrome-launcher'],
-    singleRun: true
-  })
-}
diff --git a/test/transition/package.json b/test/transition/package.json
deleted file mode 100644
index 0967ef424bc..00000000000
--- a/test/transition/package.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/test/transition/transition-group.spec.ts b/test/transition/transition-group.spec.ts
deleted file mode 100644
index 2c1905dbb7e..00000000000
--- a/test/transition/transition-group.spec.ts
+++ /dev/null
@@ -1,392 +0,0 @@
-import Vue from 'vue'
-import { injectStyles, waitForUpdate, nextFrame } from './helpers'
-
-describe('Transition group', () => {
-  const { duration, buffer } = injectStyles()
-
-  let el
-  beforeEach(() => {
-    el = document.createElement('div')
-    document.body.appendChild(el)
-  })
-
-  function createBasicVM(useIs?, appear = false) {
-    const vm = new Vue({
-      template: `
-          <div>
-            ${
-              useIs
-                ? `<span is="transition-group">`
-                : `<transition-group${appear ? ` appear` : ``}>`
-            }
-              <div v-for="item in items" :key="item" class="test">{{ item }}</div>
-            ${useIs ? `</span>` : `</transition-group>`}
-          </div>
-        `,
-      data: {
-        items: ['a', 'b', 'c']
-      }
-    }).$mount(el)
-    if (!appear) {
-      expect(vm.$el.innerHTML).toBe(
-        `<span>` +
-          vm.items.map(i => `<div class="test">${i}</div>`).join('') +
-          `</span>`
-      )
-    }
-    return vm
-  }
-
-  it('enter', done => {
-    const vm = createBasicVM()
-    vm.items.push('d', 'e')
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        `<span>` +
-          ['a', 'b', 'c'].map(i => `<div class="test">${i}</div>`).join('') +
-          `<div class="test v-enter v-enter-active">d</div>` +
-          `<div class="test v-enter v-enter-active">e</div>` +
-          `</span>`
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            ['a', 'b', 'c'].map(i => `<div class="test">${i}</div>`).join('') +
-            `<div class="test v-enter-active v-enter-to">d</div>` +
-            `<div class="test v-enter-active v-enter-to">e</div>` +
-            `</span>`
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            vm.items.map(i => `<div class="test">${i}</div>`).join('') +
-            `</span>`
-        )
-      })
-      .then(done)
-  })
-
-  it('leave', done => {
-    const vm = createBasicVM()
-    vm.items = ['b']
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        `<span>` +
-          `<div class="test v-leave v-leave-active">a</div>` +
-          `<div class="test">b</div>` +
-          `<div class="test v-leave v-leave-active">c</div>` +
-          `</span>`
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            `<div class="test v-leave-active v-leave-to">a</div>` +
-            `<div class="test">b</div>` +
-            `<div class="test v-leave-active v-leave-to">c</div>` +
-            `</span>`
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            vm.items.map(i => `<div class="test">${i}</div>`).join('') +
-            `</span>`
-        )
-      })
-      .then(done)
-  })
-
-  it('enter + leave', done => {
-    const vm = createBasicVM()
-    vm.items = ['b', 'c', 'd']
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        `<span>` +
-          `<div class="test v-leave v-leave-active">a</div>` +
-          `<div class="test">b</div>` +
-          `<div class="test">c</div>` +
-          `<div class="test v-enter v-enter-active">d</div>` +
-          `</span>`
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            `<div class="test v-leave-active v-leave-to">a</div>` +
-            `<div class="test">b</div>` +
-            `<div class="test">c</div>` +
-            `<div class="test v-enter-active v-enter-to">d</div>` +
-            `</span>`
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            vm.items.map(i => `<div class="test">${i}</div>`).join('') +
-            `</span>`
-        )
-      })
-      .then(done)
-  })
-
-  it('use with "is" attribute', done => {
-    const vm = createBasicVM(true)
-    vm.items = ['b', 'c', 'd']
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        `<span>` +
-          `<div class="test v-leave v-leave-active">a</div>` +
-          `<div class="test">b</div>` +
-          `<div class="test">c</div>` +
-          `<div class="test v-enter v-enter-active">d</div>` +
-          `</span>`
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            `<div class="test v-leave-active v-leave-to">a</div>` +
-            `<div class="test">b</div>` +
-            `<div class="test">c</div>` +
-            `<div class="test v-enter-active v-enter-to">d</div>` +
-            `</span>`
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            vm.items.map(i => `<div class="test">${i}</div>`).join('') +
-            `</span>`
-        )
-      })
-      .then(done)
-  })
-
-  it('appear', done => {
-    const vm = createBasicVM(false, true /* appear */)
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        `<span>` +
-          vm.items
-            .map(i => `<div class="test v-enter v-enter-active">${i}</div>`)
-            .join('') +
-          `</span>`
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            vm.items
-              .map(
-                i => `<div class="test v-enter-active v-enter-to">${i}</div>`
-              )
-              .join('') +
-            `</span>`
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            vm.items.map(i => `<div class="test">${i}</div>`).join('') +
-            `</span>`
-        )
-      })
-      .then(done)
-  })
-
-  it('events', done => {
-    let next
-    const beforeEnterSpy = jasmine.createSpy()
-    const afterEnterSpy = jasmine.createSpy()
-    const afterLeaveSpy = jasmine.createSpy()
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition-group @before-enter="beforeEnter" @after-enter="afterEnter" @after-leave="afterLeave">
-              <div v-for="item in items" :key="item" class="test">{{ item }}</div>
-            </transition-group>
-          </div>
-        `,
-      data: {
-        items: ['a', 'b', 'c']
-      },
-      methods: {
-        beforeEnter(el) {
-          expect(el.textContent).toBe('d')
-          beforeEnterSpy()
-        },
-        afterEnter(el) {
-          expect(el.textContent).toBe('d')
-          afterEnterSpy()
-          next()
-        },
-        afterLeave(el) {
-          expect(el.textContent).toBe('a')
-          afterLeaveSpy()
-          next()
-        }
-      }
-    }).$mount(el)
-
-    vm.items.push('d')
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        `<span>` +
-          `<div class="test">a</div>` +
-          `<div class="test">b</div>` +
-          `<div class="test">c</div>` +
-          `<div class="test v-enter v-enter-active">d</div>` +
-          `</span>`
-      )
-      expect(beforeEnterSpy.calls.count()).toBe(1)
-    })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            `<div class="test">a</div>` +
-            `<div class="test">b</div>` +
-            `<div class="test">c</div>` +
-            `<div class="test">d</div>` +
-            `</span>`
-        )
-        expect(afterEnterSpy.calls.count()).toBe(1)
-        vm.items.shift()
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<span>` +
-            `<div class="test">b</div>` +
-            `<div class="test">c</div>` +
-            `<div class="test">d</div>` +
-            `</span>`
-        )
-        expect(afterLeaveSpy.calls.count()).toBe(1)
-      })
-      .then(done)
-  })
-
-  it('move', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition-group name="group">
-              <div v-for="item in items" :key="item" class="test">{{ item }}</div>
-            </transition-group>
-          </div>
-        `,
-      data: {
-        items: ['a', 'b', 'c']
-      }
-    }).$mount(el)
-
-    vm.items = ['d', 'b', 'a']
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
-        `<span>` +
-          `<div class="test group-enter group-enter-active">d</div>` +
-          `<div class="test">b</div>` +
-          `<div class="test group-move">a</div>` +
-          `<div class="test group-leave group-leave-active group-move">c</div>` +
-          `</span>`
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
-          `<span>` +
-            `<div class="test group-enter-active group-enter-to">d</div>` +
-            `<div class="test">b</div>` +
-            `<div class="test group-move">a</div>` +
-            `<div class="test group-leave-active group-move group-leave-to">c</div>` +
-            `</span>`
-        )
-      })
-      .thenWaitFor(duration * 2)
-      .then(() => {
-        expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
-          `<span>` +
-            `<div class="test">d</div>` +
-            `<div class="test">b</div>` +
-            `<div class="test">a</div>` +
-            `</span>`
-        )
-      })
-      .then(done)
-  })
-
-  it('warn unkeyed children', () => {
-    new Vue({
-      template: `<div><transition-group><div v-for="i in 3"></div></transition-group></div>`
-    }).$mount()
-    expect(
-      '<transition-group> children must be keyed: <div>'
-    ).toHaveBeenWarned()
-  })
-
-  // GitHub issue #6006
-  it('should work with dynamic name', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition-group :name="name">
-              <div v-for="item in items" :key="item">{{ item }}</div>
-            </transition-group>
-          </div>
-        `,
-      data: {
-        items: ['a', 'b', 'c'],
-        name: 'group'
-      }
-    }).$mount(el)
-
-    vm.name = 'invalid-name'
-    vm.items = ['b', 'c', 'a']
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
-        `<span>` + `<div>b</div>` + `<div>c</div>` + `<div>a</div>` + `</span>`
-      )
-      vm.name = 'group'
-      vm.items = ['a', 'b', 'c']
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
-          `<span>` +
-            `<div class="group-move">a</div>` +
-            `<div class="group-move">b</div>` +
-            `<div class="group-move">c</div>` +
-            `</span>`
-        )
-      })
-      .thenWaitFor(duration * 2 + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
-          `<span>` +
-            `<div>a</div>` +
-            `<div>b</div>` +
-            `<div>c</div>` +
-            `</span>`
-        )
-      })
-      .then(done)
-  })
-})
diff --git a/test/transition/transition-mode.spec.ts b/test/transition/transition-mode.spec.ts
deleted file mode 100644
index 779dd33f7e2..00000000000
--- a/test/transition/transition-mode.spec.ts
+++ /dev/null
@@ -1,685 +0,0 @@
-import Vue from 'vue'
-import { injectStyles, waitForUpdate, nextFrame } from './helpers'
-
-describe('Transition mode', () => {
-  const { duration, buffer } = injectStyles()
-  const components = {
-    one: { template: '<div>one</div>' },
-    two: { template: '<div>two</div>' }
-  }
-
-  let el
-  beforeEach(() => {
-    el = document.createElement('div')
-    document.body.appendChild(el)
-  })
-
-  it('dynamic components, simultaneous', done => {
-    const vm = new Vue({
-      template: `<div>
-          <transition>
-            <component :is="view" class="test">
-            </component>
-          </transition>
-        </div>`,
-      data: { view: 'one' },
-      components
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test v-leave v-leave-active">one</div>' +
-          '<div class="test v-enter v-enter-active">two</div>'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test v-leave-active v-leave-to">one</div>' +
-            '<div class="test v-enter-active v-enter-to">two</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-      })
-      .then(done)
-  })
-
-  it('dynamic components, out-in', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="out-in" @after-leave="afterLeave">
-            <component :is="view" class="test">
-            </component>
-          </transition>
-        </div>`,
-      data: { view: 'one' },
-      components,
-      methods: {
-        afterLeave() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test test-leave test-leave-active">one</div><!---->'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">one</div><!---->'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter test-enter-active">two</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter-active test-enter-to">two</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-      })
-      .then(done)
-  })
-
-  // #3440
-  it('dynamic components, out-in (with extra re-render)', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="out-in" @after-leave="afterLeave">
-            <component :is="view" class="test">
-            </component>
-          </transition>
-        </div>`,
-      data: { view: 'one' },
-      components,
-      methods: {
-        afterLeave() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test test-leave test-leave-active">one</div><!---->'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">one</div><!---->'
-        )
-        // Force re-render before the element finishes leaving
-        // this should not cause the incoming element to enter early
-        vm.$forceUpdate()
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter test-enter-active">two</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter-active test-enter-to">two</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-      })
-      .then(done)
-  })
-
-  it('dynamic components, in-out', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="in-out" @after-enter="afterEnter">
-            <component :is="view" class="test">
-            </component>
-          </transition>
-        </div>`,
-      data: { view: 'one' },
-      components,
-      methods: {
-        afterEnter() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test">one</div>' +
-          '<div class="test test-enter test-enter-active">two</div>'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">one</div>' +
-            '<div class="test test-enter-active test-enter-to">two</div>'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">one</div>' + '<div class="test">two</div>'
-        )
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave test-leave-active">one</div>' +
-            '<div class="test">two</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">one</div>' +
-            '<div class="test">two</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-      })
-      .then(done)
-  })
-
-  it('dynamic components, in-out with early cancel', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="in-out" @after-enter="afterEnter">
-            <component :is="view" class="test"></component>
-          </transition>
-        </div>`,
-      data: { view: 'one' },
-      components,
-      methods: {
-        afterEnter() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test">one</div>' +
-          '<div class="test test-enter test-enter-active">two</div>'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">one</div>' +
-            '<div class="test test-enter-active test-enter-to">two</div>'
-        )
-        // switch again before enter finishes,
-        // this cancels both enter and leave.
-        vm.view = 'one'
-      })
-      .then(() => {
-        // 1. the pending leaving "one" should be removed instantly.
-        // 2. the entering "two" should be placed into its final state instantly.
-        // 3. a new "one" is created and entering
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">two</div>' +
-            '<div class="test test-enter test-enter-active">one</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">two</div>' +
-            '<div class="test test-enter-active test-enter-to">one</div>'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">two</div>' + '<div class="test">one</div>'
-        )
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave test-leave-active">two</div>' +
-            '<div class="test">one</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">two</div>' +
-            '<div class="test">one</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">one</div>')
-      })
-      .then(done)
-  })
-
-  it('normal elements with different keys, simultaneous', done => {
-    const vm = new Vue({
-      template: `<div>
-          <transition>
-            <div :key="view" class="test">{{view}}</div>
-          </transition>
-        </div>`,
-      data: { view: 'one' },
-      components
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test v-leave v-leave-active">one</div>' +
-          '<div class="test v-enter v-enter-active">two</div>'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test v-leave-active v-leave-to">one</div>' +
-            '<div class="test v-enter-active v-enter-to">two</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-      })
-      .then(done)
-  })
-
-  it('normal elements with different keys, out-in', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="out-in" @after-leave="afterLeave">
-            <div :key="view" class="test">{{view}}</div>
-          </transition>
-        </div>`,
-      data: { view: 'one' },
-      components,
-      methods: {
-        afterLeave() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test test-leave test-leave-active">one</div><!---->'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">one</div><!---->'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter test-enter-active">two</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter-active test-enter-to">two</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-      })
-      .then(done)
-  })
-
-  it('normal elements with different keys, in-out', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="in-out" @after-enter="afterEnter">
-            <div :key="view" class="test">{{view}}</div>
-          </transition>
-        </div>`,
-      data: { view: 'one' },
-      components,
-      methods: {
-        afterEnter() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test">one</div>' +
-          '<div class="test test-enter test-enter-active">two</div>'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">one</div>' +
-            '<div class="test test-enter-active test-enter-to">two</div>'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">one</div>' + '<div class="test">two</div>'
-        )
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave test-leave-active">one</div>' +
-            '<div class="test">two</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">one</div>' +
-            '<div class="test">two</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-      })
-      .then(done)
-  })
-
-  it('transition out-in on async component (resolve before leave complete)', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test-anim" mode="out-in">
-              <component-a v-if="ok"></component-a>
-              <component-b v-else></component-b>
-            </transition>
-          </div>
-        `,
-      components: {
-        componentA: resolve => {
-          setTimeout(() => {
-            resolve({ template: '<div><h1>component A</h1></div>' })
-            next1()
-          }, duration / 2)
-        },
-        componentB: resolve => {
-          setTimeout(() => {
-            resolve({ template: '<div><h1>component B</h1></div>' })
-          }, duration / 2)
-        }
-      },
-      data: {
-        ok: true
-      }
-    }).$mount(el)
-
-    expect(vm.$el.innerHTML).toBe('<!---->')
-
-    function next1() {
-      Vue.nextTick(() => {
-        expect(vm.$el.children.length).toBe(1)
-        expect(vm.$el.textContent).toBe('component A')
-        expect(vm.$el.children[0].className).toBe(
-          'test-anim-enter test-anim-enter-active'
-        )
-        nextFrame(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test-anim-enter-active test-anim-enter-to'
-          )
-          setTimeout(() => {
-            expect(vm.$el.children[0].className).toBe('')
-            vm.ok = false
-            next2()
-          }, duration + buffer)
-        })
-      })
-    }
-
-    function next2() {
-      waitForUpdate(() => {
-        expect(vm.$el.children.length).toBe(1)
-        expect(vm.$el.textContent).toBe('component A')
-        expect(vm.$el.children[0].className).toBe(
-          'test-anim-leave test-anim-leave-active'
-        )
-      })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test-anim-leave-active test-anim-leave-to'
-          )
-        })
-        .thenWaitFor(duration + buffer)
-        .then(() => {
-          expect(vm.$el.children.length).toBe(1)
-          expect(vm.$el.textContent).toBe('component B')
-          expect(vm.$el.children[0].className).toMatch('test-anim-enter-active')
-        })
-        .thenWaitFor(duration * 2)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe('')
-        })
-        .then(done)
-    }
-  })
-
-  it('transition out-in on async component (resolve after leave complete)', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test-anim" mode="out-in">
-              <component-a v-if="ok"></component-a>
-              <component-b v-else></component-b>
-            </transition>
-          </div>
-        `,
-      components: {
-        componentA: { template: '<div><h1>component A</h1></div>' },
-        componentB: resolve => {
-          setTimeout(() => {
-            resolve({ template: '<div><h1>component B</h1></div>' })
-            Vue.nextTick(next)
-          }, (duration + buffer) * 1.7)
-        }
-      },
-      data: {
-        ok: true
-      }
-    }).$mount(el)
-
-    expect(vm.$el.innerHTML).toBe('<div><h1>component A</h1></div>')
-
-    let next
-
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children.length).toBe(1)
-      expect(vm.$el.textContent).toBe('component A')
-      expect(vm.$el.children[0].className).toBe(
-        'test-anim-leave test-anim-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test-anim-leave-active test-anim-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        expect(vm.$el.innerHTML).toBe('<!---->')
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.children.length).toBe(1)
-        expect(vm.$el.textContent).toBe('component B')
-        expect(vm.$el.children[0].className).toBe(
-          'test-anim-enter test-anim-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test-anim-enter-active test-anim-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(1)
-        expect(vm.$el.textContent).toBe('component B')
-        expect(vm.$el.children[0].className).toBe('')
-      })
-      .then(done)
-  })
-
-  it('transition in-out on async component', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test-anim" mode="in-out">
-              <component-a v-if="ok"></component-a>
-              <component-b v-else></component-b>
-            </transition>
-          </div>
-        `,
-      components: {
-        componentA: resolve => {
-          setTimeout(() => {
-            resolve({ template: '<div><h1>component A</h1></div>' })
-            next1()
-          }, duration / 2)
-        },
-        componentB: resolve => {
-          setTimeout(() => {
-            resolve({ template: '<div><h1>component B</h1></div>' })
-            next2()
-          }, duration / 2)
-        }
-      },
-      data: {
-        ok: true
-      }
-    }).$mount(el)
-
-    expect(vm.$el.innerHTML).toBe('<!---->')
-
-    function next1() {
-      Vue.nextTick(() => {
-        expect(vm.$el.children.length).toBe(1)
-        expect(vm.$el.textContent).toBe('component A')
-        expect(vm.$el.children[0].className).toBe(
-          'test-anim-enter test-anim-enter-active'
-        )
-        nextFrame(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test-anim-enter-active test-anim-enter-to'
-          )
-          setTimeout(() => {
-            expect(vm.$el.children[0].className).toBe('')
-            vm.ok = false
-          }, duration + buffer)
-        })
-      })
-    }
-
-    function next2() {
-      waitForUpdate(() => {
-        expect(vm.$el.children.length).toBe(2)
-        expect(vm.$el.textContent).toBe('component Acomponent B')
-        expect(vm.$el.children[0].className).toBe('')
-        expect(vm.$el.children[1].className).toBe(
-          'test-anim-enter test-anim-enter-active'
-        )
-      })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[1].className).toBe(
-            'test-anim-enter-active test-anim-enter-to'
-          )
-        })
-        .thenWaitFor(duration + buffer)
-        .then(() => {
-          expect(vm.$el.children.length).toBe(2)
-          expect(vm.$el.textContent).toBe('component Acomponent B')
-          expect(vm.$el.children[0].className).toMatch('test-anim-leave-active')
-          expect(vm.$el.children[1].className).toBe('')
-        })
-        .thenWaitFor(duration + buffer * 2)
-        .then(() => {
-          expect(vm.$el.children.length).toBe(1)
-          expect(vm.$el.textContent).toBe('component B')
-          expect(vm.$el.children[0].className).toBe('')
-        })
-        .then(done)
-    }
-  })
-
-  it('warn invalid mode', () => {
-    new Vue({
-      template: '<transition mode="foo"><div>123</div></transition>'
-    }).$mount()
-    expect('invalid <transition> mode: foo').toHaveBeenWarned()
-  })
-})
diff --git a/test/transition/transition-with-keep-alive.spec.ts b/test/transition/transition-with-keep-alive.spec.ts
deleted file mode 100644
index 128255e0981..00000000000
--- a/test/transition/transition-with-keep-alive.spec.ts
+++ /dev/null
@@ -1,654 +0,0 @@
-import Vue from 'vue'
-import { injectStyles, waitForUpdate, nextFrame } from './helpers'
-
-describe('Transition w/ KeepAlive', () => {
-  const { duration, buffer } = injectStyles()
-
-  let components, one, two, el
-  beforeEach(() => {
-    one = {
-      template: '<div>one</div>',
-      created: jasmine.createSpy(),
-      mounted: jasmine.createSpy(),
-      activated: jasmine.createSpy(),
-      deactivated: jasmine.createSpy(),
-      destroyed: jasmine.createSpy()
-    }
-    two = {
-      template: '<div>two</div>',
-      created: jasmine.createSpy(),
-      mounted: jasmine.createSpy(),
-      activated: jasmine.createSpy(),
-      deactivated: jasmine.createSpy(),
-      destroyed: jasmine.createSpy()
-    }
-    components = {
-      one,
-      two
-    }
-    el = document.createElement('div')
-    document.body.appendChild(el)
-  })
-
-  function assertHookCalls(component, callCounts) {
-    expect([
-      component.created.calls.count(),
-      component.mounted.calls.count(),
-      component.activated.calls.count(),
-      component.deactivated.calls.count(),
-      component.destroyed.calls.count()
-    ]).toEqual(callCounts)
-  }
-
-  it('with transition-mode out-in', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="out-in" @after-leave="afterLeave">
-            <keep-alive>
-              <component :is="view" class="test"></component>
-            </keep-alive>
-          </transition>
-        </div>`,
-      data: {
-        view: 'one'
-      },
-      components,
-      methods: {
-        afterLeave() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    assertHookCalls(one, [1, 1, 1, 0, 0])
-    assertHookCalls(two, [0, 0, 0, 0, 0])
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test test-leave test-leave-active">one</div><!---->'
-      )
-      assertHookCalls(one, [1, 1, 1, 1, 0])
-      assertHookCalls(two, [0, 0, 0, 0, 0])
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">one</div><!---->'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter test-enter-active">two</div>'
-        )
-        assertHookCalls(one, [1, 1, 1, 1, 0])
-        assertHookCalls(two, [1, 1, 1, 0, 0])
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter-active test-enter-to">two</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-        assertHookCalls(one, [1, 1, 1, 1, 0])
-        assertHookCalls(two, [1, 1, 1, 0, 0])
-      })
-      .then(() => {
-        vm.view = 'one'
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave test-leave-active">two</div><!---->'
-        )
-        assertHookCalls(one, [1, 1, 1, 1, 0])
-        assertHookCalls(two, [1, 1, 1, 1, 0])
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">two</div><!---->'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter test-enter-active">one</div>'
-        )
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 1, 1, 0])
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter-active test-enter-to">one</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">one</div>')
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 1, 1, 0])
-      })
-      .then(done)
-  })
-
-  it('with transition-mode out-in + include', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="out-in" @after-leave="afterLeave">
-            <keep-alive include="one">
-              <component :is="view" class="test"></component>
-            </keep-alive>
-          </transition>
-        </div>`,
-      data: {
-        view: 'one'
-      },
-      components,
-      methods: {
-        afterLeave() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    assertHookCalls(one, [1, 1, 1, 0, 0])
-    assertHookCalls(two, [0, 0, 0, 0, 0])
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test test-leave test-leave-active">one</div><!---->'
-      )
-      assertHookCalls(one, [1, 1, 1, 1, 0])
-      assertHookCalls(two, [0, 0, 0, 0, 0])
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">one</div><!---->'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter test-enter-active">two</div>'
-        )
-        assertHookCalls(one, [1, 1, 1, 1, 0])
-        assertHookCalls(two, [1, 1, 0, 0, 0])
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter-active test-enter-to">two</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-        assertHookCalls(one, [1, 1, 1, 1, 0])
-        assertHookCalls(two, [1, 1, 0, 0, 0])
-      })
-      .then(() => {
-        vm.view = 'one'
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave test-leave-active">two</div><!---->'
-        )
-        assertHookCalls(one, [1, 1, 1, 1, 0])
-        assertHookCalls(two, [1, 1, 0, 0, 1])
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">two</div><!---->'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter test-enter-active">one</div>'
-        )
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 0, 0, 1])
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter-active test-enter-to">one</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">one</div>')
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 0, 0, 1])
-      })
-      .then(done)
-  })
-
-  it('with transition-mode in-out', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="in-out" @after-enter="afterEnter">
-            <keep-alive>
-              <component :is="view" class="test"></component>
-            </keep-alive>
-          </transition>
-        </div>`,
-      data: {
-        view: 'one'
-      },
-      components,
-      methods: {
-        afterEnter() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    assertHookCalls(one, [1, 1, 1, 0, 0])
-    assertHookCalls(two, [0, 0, 0, 0, 0])
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test">one</div>' +
-          '<div class="test test-enter test-enter-active">two</div>'
-      )
-      assertHookCalls(one, [1, 1, 1, 1, 0])
-      assertHookCalls(two, [1, 1, 1, 0, 0])
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">one</div>' +
-            '<div class="test test-enter-active test-enter-to">two</div>'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">one</div>' + '<div class="test">two</div>'
-        )
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave test-leave-active">one</div>' +
-            '<div class="test">two</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">one</div>' +
-            '<div class="test">two</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-        assertHookCalls(one, [1, 1, 1, 1, 0])
-        assertHookCalls(two, [1, 1, 1, 0, 0])
-      })
-      .then(() => {
-        vm.view = 'one'
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">two</div>' +
-            '<div class="test test-enter test-enter-active">one</div>'
-        )
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 1, 1, 0])
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">two</div>' +
-            '<div class="test test-enter-active test-enter-to">one</div>'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">two</div>' + '<div class="test">one</div>'
-        )
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave test-leave-active">two</div>' +
-            '<div class="test">one</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">two</div>' +
-            '<div class="test">one</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">one</div>')
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 1, 1, 0])
-      })
-      .then(done)
-  })
-
-  it('dynamic components, in-out with early cancel', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="in-out" @after-enter="afterEnter">
-            <keep-alive>
-              <component :is="view" class="test"></component>
-            </keep-alive>
-          </transition>
-        </div>`,
-      data: { view: 'one' },
-      components,
-      methods: {
-        afterEnter() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('one')
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test">one</div>' +
-          '<div class="test test-enter test-enter-active">two</div>'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">one</div>' +
-            '<div class="test test-enter-active test-enter-to">two</div>'
-        )
-        // switch again before enter finishes,
-        // this cancels both enter and leave.
-        vm.view = 'one'
-      })
-      .then(() => {
-        // 1. the pending leaving "one" should be removed instantly.
-        // 2. the entering "two" should be placed into its final state instantly.
-        // 3. a new "one" is created and entering
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">two</div>' +
-            '<div class="test test-enter test-enter-active">one</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">two</div>' +
-            '<div class="test test-enter-active test-enter-to">one</div>'
-        )
-      })
-      .thenWaitFor(_next => {
-        next = _next
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test">two</div>' + '<div class="test">one</div>'
-        )
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave test-leave-active">two</div>' +
-            '<div class="test">one</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">two</div>' +
-            '<div class="test">one</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">one</div>')
-      })
-      .then(done)
-  })
-
-  // #4339
-  it('component with inner transition', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <keep-alive>
-              <component ref="test" :is="view"></component>
-            </keep-alive>
-          </div>
-        `,
-      data: { view: 'foo' },
-      components: {
-        foo: {
-          template: '<transition><div class="test">foo</div></transition>'
-        },
-        bar: {
-          template:
-            '<transition name="test"><div class="test">bar</div></transition>'
-        }
-      }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.view = 'bar'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test v-leave v-leave-active">foo</div>' +
-          '<div class="test test-enter test-enter-active">bar</div>'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test v-leave-active v-leave-to">foo</div>' +
-            '<div class="test test-enter-active test-enter-to">bar</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">bar</div>')
-        vm.view = 'foo'
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave test-leave-active">bar</div>' +
-            '<div class="test v-enter v-enter-active">foo</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-leave-active test-leave-to">bar</div>' +
-            '<div class="test v-enter-active v-enter-to">foo</div>'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-      })
-      .then(done)
-  })
-
-  it('async components with transition-mode out-in', done => {
-    const barResolve = jasmine.createSpy()
-    let next
-    const foo = resolve => {
-      setTimeout(() => {
-        resolve(one)
-        Vue.nextTick(next)
-      }, duration / 2)
-    }
-    const bar = resolve => {
-      setTimeout(() => {
-        resolve(two)
-        barResolve()
-      }, duration / 2)
-    }
-    components = {
-      foo,
-      bar
-    }
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" mode="out-in" @after-enter="afterEnter" @after-leave="afterLeave">
-            <keep-alive>
-              <component :is="view" class="test"></component>
-            </keep-alive>
-          </transition>
-        </div>`,
-      data: {
-        view: 'foo'
-      },
-      components,
-      methods: {
-        afterEnter() {
-          next()
-        },
-        afterLeave() {
-          next()
-        }
-      }
-    }).$mount(el)
-    expect(vm.$el.textContent).toBe('')
-    next = () => {
-      assertHookCalls(one, [1, 1, 1, 0, 0])
-      assertHookCalls(two, [0, 0, 0, 0, 0])
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test test-enter test-enter-active">one</div>'
-        )
-      })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<div class="test test-enter-active test-enter-to">one</div>'
-          )
-        })
-        .thenWaitFor(_next => {
-          next = _next
-        })
-        .then(() => {
-          // foo afterEnter get called
-          expect(vm.$el.innerHTML).toBe('<div class="test">one</div>')
-          vm.view = 'bar'
-        })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          assertHookCalls(one, [1, 1, 1, 1, 0])
-          assertHookCalls(two, [0, 0, 0, 0, 0])
-          expect(vm.$el.innerHTML).toBe(
-            '<div class="test test-leave-active test-leave-to">one</div><!---->'
-          )
-        })
-        .thenWaitFor(_next => {
-          next = _next
-        })
-        .then(() => {
-          // foo afterLeave get called
-          // and bar has already been resolved before afterLeave get called
-          expect(barResolve.calls.count()).toBe(1)
-          expect(vm.$el.innerHTML).toBe('<!---->')
-        })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<div class="test test-enter test-enter-active">two</div>'
-          )
-          assertHookCalls(one, [1, 1, 1, 1, 0])
-          assertHookCalls(two, [1, 1, 1, 0, 0])
-        })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<div class="test test-enter-active test-enter-to">two</div>'
-          )
-        })
-        .thenWaitFor(_next => {
-          next = _next
-        })
-        .then(() => {
-          // bar afterEnter get called
-          expect(vm.$el.innerHTML).toBe('<div class="test">two</div>')
-        })
-        .then(done)
-    }
-  })
-
-  // #10083
-  it('should not attach event handler repeatedly', done => {
-    const vm = new Vue({
-      template: `
-          <keep-alive>
-            <btn v-if="showBtn" @click.native="add" />
-          </keep-alive>
-        `,
-      data: { showBtn: true, n: 0 },
-      methods: {
-        add() {
-          this.n++
-        }
-      },
-      components: {
-        btn: { template: '<button>add 1</button>' }
-      }
-    }).$mount()
-
-    const btn = vm.$el
-    expect(vm.n).toBe(0)
-    btn.click()
-    expect(vm.n).toBe(1)
-    vm.showBtn = false
-    waitForUpdate(() => {
-      vm.showBtn = true
-    })
-      .then(() => {
-        btn.click()
-        expect(vm.n).toBe(2)
-      })
-      .then(done)
-  })
-})
diff --git a/test/transition/transition.spec.ts b/test/transition/transition.spec.ts
deleted file mode 100644
index bff2e0fefd7..00000000000
--- a/test/transition/transition.spec.ts
+++ /dev/null
@@ -1,1842 +0,0 @@
-import Vue from 'vue'
-import { injectStyles, waitForUpdate, nextFrame } from './helpers'
-
-describe('Transition basic', () => {
-  const { duration, buffer } = injectStyles() as {
-    duration: number
-    buffer: number
-  }
-  const explicitDuration = duration * 2
-
-  let el
-  beforeEach(() => {
-    el = document.createElement('div')
-    document.body.appendChild(el)
-  })
-
-  it('basic transition', done => {
-    const vm = new Vue({
-      template:
-        '<div><transition><div v-if="ok" class="test">foo</div></transition></div>',
-      data: { ok: true }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test v-leave-active v-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test v-enter-active v-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('named transition', done => {
-    const vm = new Vue({
-      template:
-        '<div><transition name="test"><div v-if="ok" class="test">foo</div></transition></div>',
-      data: { ok: true }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-leave test-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter test-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('custom transition classes', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition
-              enter-class="hello"
-              enter-active-class="hello-active"
-              enter-to-class="hello-to"
-              leave-class="bye"
-              leave-to-class="bye-to"
-              leave-active-class="byebye active more ">
-              <div v-if="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe('test bye byebye active more')
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test byebye active more bye-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test hello hello-active')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test hello-active hello-to')
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('dynamic transition', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition :name="trans">
-              <div v-if="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: {
-        ok: true,
-        trans: 'test'
-      }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-leave test-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-        vm.trans = 'changed'
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test changed-enter changed-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test changed-enter-active changed-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('inline transition object', done => {
-    const enter = jasmine.createSpy()
-    const leave = jasmine.createSpy()
-    const vm = new Vue({
-      render(h) {
-        return h('div', null, [
-          h(
-            'transition',
-            {
-              props: {
-                name: 'inline',
-                enterClass: 'hello',
-                enterToClass: 'hello-to',
-                enterActiveClass: 'hello-active',
-                leaveClass: 'bye',
-                leaveToClass: 'bye-to',
-                leaveActiveClass: 'byebye active'
-              },
-              on: {
-                enter,
-                leave
-              }
-            },
-            this.ok ? [h('div', { class: 'test' }, 'foo')] : undefined
-          )
-        ])
-      },
-      data: { ok: true }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe('test bye byebye active')
-      expect(leave).toHaveBeenCalled()
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test byebye active bye-to')
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test hello hello-active')
-        expect(enter).toHaveBeenCalled()
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test hello-active hello-to')
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('transition events', done => {
-    const onLeaveSpy = jasmine.createSpy()
-    const onEnterSpy = jasmine.createSpy()
-    const beforeLeaveSpy = jasmine.createSpy()
-    const beforeEnterSpy = jasmine.createSpy()
-    const afterLeaveSpy = jasmine.createSpy()
-    const afterEnterSpy = jasmine.createSpy()
-
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition
-              name="test"
-              @before-enter="beforeEnter"
-              @enter="enter"
-              @after-enter="afterEnter"
-              @before-leave="beforeLeave"
-              @leave="leave"
-              @after-leave="afterLeave">
-              <div v-if="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true },
-      methods: {
-        beforeLeave: el => {
-          expect(el).toBe(vm.$el.children[0])
-          expect(el.className).toBe('test')
-          beforeLeaveSpy(el)
-        },
-        leave: el => onLeaveSpy(el),
-        afterLeave: el => afterLeaveSpy(el),
-        beforeEnter: el => {
-          expect(vm.$el.contains(el)).toBe(false)
-          expect(el.className).toBe('test')
-          beforeEnterSpy(el)
-        },
-        enter: el => {
-          expect(vm.$el.contains(el)).toBe(true)
-          onEnterSpy(el)
-        },
-        afterEnter: el => afterEnterSpy(el)
-      }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-
-    let _el = vm.$el.children[0]
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(beforeLeaveSpy).toHaveBeenCalledWith(_el)
-      expect(onLeaveSpy).toHaveBeenCalledWith(_el)
-      expect(vm.$el.children[0].className).toBe(
-        'test test-leave test-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(afterLeaveSpy).not.toHaveBeenCalled()
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(afterLeaveSpy).toHaveBeenCalledWith(_el)
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        _el = vm.$el.children[0]
-        expect(beforeEnterSpy).toHaveBeenCalledWith(_el)
-        expect(onEnterSpy).toHaveBeenCalledWith(_el)
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter test-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(afterEnterSpy).not.toHaveBeenCalled()
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(afterEnterSpy).toHaveBeenCalledWith(_el)
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('transition events (v-show)', done => {
-    const onLeaveSpy = jasmine.createSpy()
-    const onEnterSpy = jasmine.createSpy()
-    const beforeLeaveSpy = jasmine.createSpy()
-    const beforeEnterSpy = jasmine.createSpy()
-    const afterLeaveSpy = jasmine.createSpy()
-    const afterEnterSpy = jasmine.createSpy()
-
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition
-              name="test"
-              @before-enter="beforeEnter"
-              @enter="enter"
-              @after-enter="afterEnter"
-              @before-leave="beforeLeave"
-              @leave="leave"
-              @after-leave="afterLeave">
-              <div v-show="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true },
-      methods: {
-        beforeLeave: el => {
-          expect(el.style.display).toBe('')
-          expect(el).toBe(vm.$el.children[0])
-          expect(el.className).toBe('test')
-          beforeLeaveSpy(el)
-        },
-        leave: el => {
-          expect(el.style.display).toBe('')
-          onLeaveSpy(el)
-        },
-        afterLeave: el => {
-          expect(el.style.display).toBe('none')
-          afterLeaveSpy(el)
-        },
-        beforeEnter: el => {
-          expect(el.className).toBe('test')
-          expect(el.style.display).toBe('none')
-          beforeEnterSpy(el)
-        },
-        enter: el => {
-          expect(el.style.display).toBe('')
-          onEnterSpy(el)
-        },
-        afterEnter: el => {
-          expect(el.style.display).toBe('')
-          afterEnterSpy(el)
-        }
-      }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-
-    let _el = vm.$el.children[0]
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(beforeLeaveSpy).toHaveBeenCalledWith(_el)
-      expect(onLeaveSpy).toHaveBeenCalledWith(_el)
-      expect(vm.$el.children[0].className).toBe(
-        'test test-leave test-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(afterLeaveSpy).not.toHaveBeenCalled()
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(afterLeaveSpy).toHaveBeenCalledWith(_el)
-        expect(vm.$el.children[0].style.display).toBe('none')
-        vm.ok = true
-      })
-      .then(() => {
-        _el = vm.$el.children[0]
-        expect(beforeEnterSpy).toHaveBeenCalledWith(_el)
-        expect(onEnterSpy).toHaveBeenCalledWith(_el)
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter test-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(afterEnterSpy).not.toHaveBeenCalled()
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(afterEnterSpy).toHaveBeenCalledWith(_el)
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('explicit user callback in JavaScript hooks', done => {
-    let next
-    const vm = new Vue({
-      template: `<div>
-          <transition name="test" @enter="enter" @leave="leave">
-            <div v-if="ok" class="test">foo</div>
-          </transition>
-        </div>`,
-      data: { ok: true },
-      methods: {
-        enter: (el, cb) => {
-          next = cb
-        },
-        leave: (el, cb) => {
-          next = cb
-        }
-      }
-    }).$mount(el)
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-leave test-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-        expect(next).toBeTruthy()
-        next()
-        expect(vm.$el.children.length).toBe(0)
-      })
-      .then(() => {
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter test-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-        expect(next).toBeTruthy()
-        next()
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('css: false', done => {
-    const enterSpy = jasmine.createSpy()
-    const leaveSpy = jasmine.createSpy()
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition :css="false" name="test" @enter="enter" @leave="leave">
-              <div v-if="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true },
-      methods: {
-        enter: enterSpy,
-        leave: leaveSpy
-      }
-    }).$mount(el)
-
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(leaveSpy).toHaveBeenCalled()
-      expect(vm.$el.innerHTML).toBe('<!---->')
-      vm.ok = true
-    })
-      .then(() => {
-        expect(enterSpy).toHaveBeenCalled()
-        expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-      })
-      .then(done)
-  })
-
-  it('no transition detected', done => {
-    const enterSpy = jasmine.createSpy()
-    const leaveSpy = jasmine.createSpy()
-    const vm = new Vue({
-      template:
-        '<div><transition name="nope" @enter="enter" @leave="leave"><div v-if="ok">foo</div></transition></div>',
-      data: { ok: true },
-      methods: {
-        enter: enterSpy,
-        leave: leaveSpy
-      }
-    }).$mount(el)
-
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(leaveSpy).toHaveBeenCalled()
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="nope-leave nope-leave-active">foo</div><!---->'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-        vm.ok = true
-      })
-      .then(() => {
-        expect(enterSpy).toHaveBeenCalled()
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="nope-enter nope-enter-active">foo</div>'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div>foo</div>')
-      })
-      .then(done)
-  })
-
-  it('enterCancelled', done => {
-    const spy = jasmine.createSpy()
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test" @enter-cancelled="enterCancelled">
-              <div v-if="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: false },
-      methods: {
-        enterCancelled: spy
-      }
-    }).$mount(el)
-
-    expect(vm.$el.innerHTML).toBe('<!---->')
-    vm.ok = true
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-enter test-enter-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration / 2)
-      .then(() => {
-        vm.ok = false
-      })
-      .then(() => {
-        expect(spy).toHaveBeenCalled()
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave test-leave-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-      })
-      .then(done)
-  })
-
-  it('should remove stale leaving elements', done => {
-    const spy = jasmine.createSpy()
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test" @after-leave="afterLeave">
-              <div v-if="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true },
-      methods: {
-        afterLeave: spy
-      }
-    }).$mount(el)
-
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-leave test-leave-active'
-      )
-    })
-      .thenWaitFor(duration / 2)
-      .then(() => {
-        vm.ok = true
-      })
-      .then(() => {
-        expect(spy).toHaveBeenCalled()
-        expect(vm.$el.children.length).toBe(1) // should have removed leaving element
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter test-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-      })
-      .then(done)
-  })
-
-  it('transition with v-show', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test">
-              <div v-show="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.textContent).toBe('foo')
-    expect(vm.$el.children[0].style.display).toBe('')
-    expect(vm.$el.children[0].className).toBe('test')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-leave test-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].style.display).toBe('none')
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].style.display).toBe('')
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter test-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('transition with v-show, inside child component', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <test v-show="ok"></test>
-          </div>
-        `,
-      data: { ok: true },
-      components: {
-        test: {
-          template: `<transition name="test"><div class="test">foo</div></transition>`
-        }
-      }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.textContent).toBe('foo')
-    expect(vm.$el.children[0].style.display).toBe('')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-leave test-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].style.display).toBe('none')
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].style.display).toBe('')
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter test-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('leaveCancelled (v-show only)', done => {
-    const spy = jasmine.createSpy()
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test" @leave-cancelled="leaveCancelled">
-              <div v-show="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true },
-      methods: {
-        leaveCancelled: spy
-      }
-    }).$mount(el)
-
-    expect(vm.$el.children[0].style.display).toBe('')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-leave test-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(10)
-      .then(() => {
-        vm.ok = true
-      })
-      .then(() => {
-        expect(spy).toHaveBeenCalled()
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter test-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].style.display).toBe('')
-      })
-      .then(done)
-  })
-
-  it('leave transition with v-show: cancelled on next frame', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test">
-              <div v-show="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true }
-    }).$mount(el)
-
-    vm.ok = false
-    waitForUpdate(() => {
-      vm.ok = true
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('enter transition with v-show: cancelled on next frame', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test">
-              <div v-show="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: false }
-    }).$mount(el)
-
-    vm.ok = true
-    waitForUpdate(() => {
-      vm.ok = false
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('animations', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test-anim">
-              <div v-if="ok">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div>foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test-anim-leave test-anim-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test-anim-leave-active test-anim-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test-anim-enter test-anim-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test-anim-enter-active test-anim-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('')
-      })
-      .then(done)
-  })
-
-  it('explicit transition type', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test-anim-long" type="animation">
-              <div v-if="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-anim-long-leave test-anim-long-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-anim-long-leave-active test-anim-long-leave-to'
-        )
-      })
-      .thenWaitFor(duration + 5)
-      .then(() => {
-        // should not end early due to transition presence
-        expect(vm.$el.children[0].className).toBe(
-          'test test-anim-long-leave-active test-anim-long-leave-to'
-        )
-      })
-      .thenWaitFor(duration + 5)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-anim-long-enter test-anim-long-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-anim-long-enter-active test-anim-long-enter-to'
-        )
-      })
-      .thenWaitFor(duration + 5)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-anim-long-enter-active test-anim-long-enter-to'
-        )
-      })
-      .thenWaitFor(duration + 5)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('transition on appear', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test"
-              appear
-              appear-class="test-appear"
-              appear-to-class="test-appear-to"
-              appear-active-class="test-appear-active">
-              <div v-if="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true }
-    }).$mount(el)
-
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-appear test-appear-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-appear-active test-appear-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('transition on appear with v-show', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition name="test" appear>
-              <div v-show="ok" class="test">foo</div>
-            </transition>
-          </div>
-        `,
-      data: { ok: true }
-    }).$mount(el)
-
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-enter test-enter-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('transition on SVG elements', done => {
-    const vm = new Vue({
-      template: `
-          <svg>
-            <transition>
-              <circle cx="0" cy="0" r="10" v-if="ok" class="test"></circle>
-            </transition>
-          </svg>
-        `,
-      data: { ok: true }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.childNodes[0].getAttribute('class')).toBe(
-        'test v-leave v-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.childNodes[0].getAttribute('class')).toBe(
-          'test v-leave-active v-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.childNodes.length).toBe(1)
-        expect(vm.$el.childNodes[0].nodeType).toBe(8) // should be an empty comment node
-        expect(vm.$el.childNodes[0].textContent).toBe('')
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.childNodes[0].getAttribute('class')).toBe(
-          'test v-enter v-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.childNodes[0].getAttribute('class')).toBe(
-          'test v-enter-active v-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('transition on child components', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition>
-              <test v-if="ok" class="test"></test>
-            </transition>
-          </div>
-        `,
-      data: { ok: true },
-      components: {
-        test: {
-          template: `
-              <transition name="test">
-                <div>foo</div>
-              </transition>
-            ` // test transition override from parent
-        }
-      }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test v-leave-active v-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test v-enter-active v-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('transition inside child component with v-if', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <test v-if="ok" class="test"></test>
-          </div>
-        `,
-      data: { ok: true },
-      components: {
-        test: {
-          template: `
-              <transition>
-                <div>foo</div>
-              </transition>
-            `
-        }
-      }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test v-leave-active v-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('transition with appear inside child component with v-if', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <test v-if="ok" class="test"></test>
-          </div>
-        `,
-      data: { ok: true },
-      components: {
-        test: {
-          template: `
-              <transition appear
-                appear-class="test-appear"
-                appear-to-class="test-appear-to"
-                appear-active-class="test-appear-active">
-                <div>foo</div>
-              </transition>
-            `
-        }
-      }
-    }).$mount(el)
-
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-appear test-appear-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-appear-active test-appear-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-        vm.ok = false
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test v-leave-active v-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-      })
-      .then(done)
-  })
-
-  it('transition inside nested child component with v-if', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <foo v-if="ok" class="test"></foo>
-          </div>
-        `,
-      data: { ok: true },
-      components: {
-        foo: {
-          template: '<bar></bar>',
-          components: {
-            bar: {
-              template: '<transition><div>foo</div></transition>'
-            }
-          }
-        }
-      }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test v-leave-active v-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('transition with appear inside nested child component with v-if', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <foo v-if="ok" class="test"></foo>
-          </div>
-        `,
-      data: { ok: true },
-      components: {
-        foo: {
-          template: '<bar></bar>',
-          components: {
-            bar: {
-              template: `
-                  <transition appear
-                    appear-class="test-appear"
-                    appear-to-class="test-appear-to"
-                    appear-active-class="test-appear-active">
-                    <div>foo</div>
-                  </transition>
-                `
-            }
-          }
-        }
-      }
-    }).$mount(el)
-
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-appear test-appear-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-appear-active test-appear-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-        vm.ok = false
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test v-leave-active v-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-      })
-      .then(done)
-  })
-
-  it('custom transition higher-order component', done => {
-    const vm = new Vue({
-      template:
-        '<div><my-transition><div v-if="ok" class="test">foo</div></my-transition></div>',
-      data: { ok: true },
-      components: {
-        'my-transition': {
-          functional: true,
-          render(h, { data, children }) {
-            ;(data.props || (data.props = {})).name = 'test'
-            return h('transition', data, children)
-          }
-        }
-      }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe(
-        'test test-leave test-leave-active'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-leave-active test-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children.length).toBe(0)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter test-enter-active'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test test-enter-active test-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-      })
-      .then(done)
-  })
-
-  it('warn when used on multiple elements', () => {
-    new Vue({
-      template: `<transition><p>1</p><p>2</p></transition>`
-    }).$mount()
-    expect(
-      `<transition> can only be used on a single element`
-    ).toHaveBeenWarned()
-  })
-
-  describe('explicit durations -', () => {
-    it('single value', done => {
-      const vm = new Vue({
-        template: `
-            <div>
-              <transition duration="${explicitDuration}">
-                <div v-if="ok" class="test">foo</div>
-              </transition>
-            </div>
-          `,
-        data: { ok: true }
-      }).$mount(el)
-
-      vm.ok = false
-
-      waitForUpdate(() => {
-        expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-      })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-leave-active v-leave-to'
-          )
-        })
-        .thenWaitFor(explicitDuration + buffer)
-        .then(() => {
-          expect(vm.$el.children.length).toBe(0)
-          vm.ok = true
-        })
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter v-enter-active'
-          )
-        })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter-active v-enter-to'
-          )
-        })
-        .thenWaitFor(explicitDuration + buffer)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe('test')
-        })
-        .then(done)
-    })
-
-    it('enter and auto leave', done => {
-      const vm = new Vue({
-        template: `
-            <div>
-              <transition :duration="{ enter: ${explicitDuration} }">
-                <div v-if="ok" class="test">foo</div>
-              </transition>
-            </div>
-          `,
-        data: { ok: true }
-      }).$mount(el)
-
-      vm.ok = false
-
-      waitForUpdate(() => {
-        expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-      })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-leave-active v-leave-to'
-          )
-        })
-        .thenWaitFor(duration + buffer)
-        .then(() => {
-          expect(vm.$el.children.length).toBe(0)
-          vm.ok = true
-        })
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter v-enter-active'
-          )
-        })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter-active v-enter-to'
-          )
-        })
-        .thenWaitFor(explicitDuration + buffer)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe('test')
-        })
-        .then(done)
-    })
-
-    it('leave and auto enter', done => {
-      const vm = new Vue({
-        template: `
-            <div>
-              <transition :duration="{ leave: ${explicitDuration} }">
-                <div v-if="ok" class="test">foo</div>
-              </transition>
-            </div>
-          `,
-        data: { ok: true }
-      }).$mount(el)
-
-      vm.ok = false
-
-      waitForUpdate(() => {
-        expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-      })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-leave-active v-leave-to'
-          )
-        })
-        .thenWaitFor(explicitDuration + buffer)
-        .then(() => {
-          expect(vm.$el.children.length).toBe(0)
-          vm.ok = true
-        })
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter v-enter-active'
-          )
-        })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter-active v-enter-to'
-          )
-        })
-        .thenWaitFor(duration + buffer)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe('test')
-        })
-        .then(done)
-    })
-
-    it('separate enter and leave', done => {
-      const enter = explicitDuration
-      const leave = explicitDuration * 2
-
-      const vm = new Vue({
-        template: `
-            <div>
-              <transition :duration="{ enter: ${enter}, leave: ${leave} }">
-                <div v-if="ok" class="test">foo</div>
-              </transition>
-            </div>
-          `,
-        data: { ok: true }
-      }).$mount(el)
-
-      vm.ok = false
-
-      waitForUpdate(() => {
-        expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-      })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-leave-active v-leave-to'
-          )
-        })
-        .thenWaitFor(leave + buffer)
-        .then(() => {
-          expect(vm.$el.children.length).toBe(0)
-          vm.ok = true
-        })
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter v-enter-active'
-          )
-        })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter-active v-enter-to'
-          )
-        })
-        .thenWaitFor(enter + buffer)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe('test')
-        })
-        .then(done)
-    })
-
-    it('enter and leave + duration change', done => {
-      const enter1 = explicitDuration * 2
-      const enter2 = explicitDuration
-      const leave1 = explicitDuration * 0.5
-      const leave2 = explicitDuration * 3
-
-      const vm = new Vue({
-        template: `
-            <div>
-              <transition :duration="{ enter: enter, leave: leave }">
-                <div v-if="ok" class="test">foo</div>
-              </transition>
-            </div>
-          `,
-        data: {
-          ok: true,
-          enter: enter1,
-          leave: leave1
-        }
-      }).$mount(el)
-
-      vm.ok = false
-
-      waitForUpdate(() => {
-        expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
-      })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-leave-active v-leave-to'
-          )
-        })
-        .thenWaitFor(leave1 + buffer)
-        .then(() => {
-          expect(vm.$el.children.length).toBe(0)
-          vm.ok = true
-        })
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter v-enter-active'
-          )
-        })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter-active v-enter-to'
-          )
-        })
-        .thenWaitFor(enter1 + buffer)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe('test')
-          vm.enter = enter2
-          vm.leave = leave2
-        })
-        .then(() => {
-          vm.ok = false
-        })
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-leave v-leave-active'
-          )
-        })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-leave-active v-leave-to'
-          )
-        })
-        .thenWaitFor(leave2 + buffer)
-        .then(() => {
-          expect(vm.$el.children.length).toBe(0)
-          vm.ok = true
-        })
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter v-enter-active'
-          )
-        })
-        .thenWaitFor(nextFrame)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe(
-            'test v-enter-active v-enter-to'
-          )
-        })
-        .thenWaitFor(enter2 + buffer)
-        .then(() => {
-          expect(vm.$el.children[0].className).toBe('test')
-        })
-        .then(done)
-    }, 10000)
-
-    it('warn invalid durations', done => {
-      const vm = new Vue({
-        template: `
-            <div>
-              <transition :duration="{ enter: NaN, leave: 'foo' }">
-                <div v-if="ok" class="test">foo</div>
-              </transition>
-            </div>
-          `,
-        data: {
-          ok: true
-        }
-      }).$mount(el)
-
-      vm.ok = false
-      waitForUpdate(() => {
-        expect(
-          `<transition> explicit leave duration is not a valid number - got "foo"`
-        ).toHaveBeenWarned()
-      })
-        .thenWaitFor(duration + buffer)
-        .then(() => {
-          vm.ok = true
-        })
-        .then(() => {
-          expect(
-            `<transition> explicit enter duration is NaN`
-          ).toHaveBeenWarned()
-        })
-        .then(done)
-    })
-  })
-
-  // #6687
-  it('transition on child components with empty root node', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <transition mode="out-in">
-              <component class="test" :is="view"></component>
-            </transition>
-          </div>
-        `,
-      data: { view: 'one' },
-      components: {
-        one: {
-          template: '<div v-if="false">one</div>'
-        },
-        two: {
-          template: '<div>two</div>'
-        }
-      }
-    }).$mount(el)
-
-    // should not apply transition on initial render by default
-    expect(vm.$el.innerHTML).toBe('<!---->')
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div class="test v-enter v-enter-active">two</div>'
-      )
-    })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test v-enter-active v-enter-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe('test')
-        vm.view = 'one'
-      })
-      .then(() => {
-        // incoming comment node is appended instantly because it doesn't have
-        // data and therefore doesn't go through the transition module.
-        expect(vm.$el.innerHTML).toBe(
-          '<div class="test v-leave v-leave-active">two</div><!---->'
-        )
-      })
-      .thenWaitFor(nextFrame)
-      .then(() => {
-        expect(vm.$el.children[0].className).toBe(
-          'test v-leave-active v-leave-to'
-        )
-      })
-      .thenWaitFor(duration + buffer)
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-      })
-      .then(done)
-  })
-
-  // #8199
-  it('should not throw error when replaced by v-html contents', done => {
-    const vm = new Vue({
-      template: `
-          <div>
-            <div v-if="ok" :class="ok">
-              <transition>
-                <span>a</span>
-              </transition>
-            </div>
-            <div v-else v-html="ok"></div>
-          </div>
-        `,
-      data: { ok: true }
-    }).$mount(el)
-
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].innerHTML).toBe('false')
-    }).then(done)
-  })
-})
diff --git a/test/tsconfig.json b/test/tsconfig.json
deleted file mode 100644
index 594e420687c..00000000000
--- a/test/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "extends": "../tsconfig.json",
-  "compilerOptions": {
-    "types": ["node", "vitest/globals"]
-  },
-  "include": ["../src", "."]
-}
diff --git a/test/unit/features/component/component-async.spec.ts b/test/unit/features/component/component-async.spec.ts
deleted file mode 100644
index ec1ec2484fe..00000000000
--- a/test/unit/features/component/component-async.spec.ts
+++ /dev/null
@@ -1,455 +0,0 @@
-import Vue from 'vue'
-
-describe('Component async', () => {
-  const oldSetTimeout = setTimeout
-  const oldClearTimeout = clearTimeout
-
-  // will contain pending timeouts set during the test iteration
-  // will contain the id of the timeout as the key, and the millisecond timeout as the value
-  // this helps to identify the timeout that is still pending
-  let timeoutsPending = {}
-
-  beforeAll(function () {
-    // @ts-expect-error
-    global.setTimeout = function (func, delay) {
-      if (delay >= 1000) {
-        // skip vitest internal timeouts
-        return
-      }
-      const id = oldSetTimeout(function () {
-        delete timeoutsPending[id]
-        func()
-      }, delay) as any
-      timeoutsPending[id] = delay
-      return id
-    }
-
-    global.clearTimeout = function (id) {
-      oldClearTimeout(id)
-      delete timeoutsPending[id]
-    }
-  })
-
-  afterAll(function () {
-    global.setTimeout = oldSetTimeout
-    global.clearTimeout = oldClearTimeout
-  })
-
-  beforeEach(() => {
-    // reset the timeouts for this iteration
-    timeoutsPending = {}
-  })
-
-  afterEach(() => {
-    // after the test is complete no timeouts that have been set up during the test should still be active
-    // compare stringified JSON for better error message containing ID and millisecond timeout
-    expect(JSON.stringify(timeoutsPending)).toEqual(JSON.stringify({}))
-  })
-
-  it('normal', done => {
-    const vm = new Vue({
-      template: '<div><test></test></div>',
-      components: {
-        test: resolve => {
-          setTimeout(() => {
-            resolve({
-              template: '<div>hi</div>'
-            })
-            // wait for parent update
-            Vue.nextTick(next)
-          }, 0)
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<!---->')
-    expect(vm.$children.length).toBe(0)
-
-    function next() {
-      expect(vm.$el.innerHTML).toBe('<div>hi</div>')
-      expect(vm.$children.length).toBe(1)
-      done()
-    }
-  })
-
-  it('resolve ES module default', done => {
-    const vm = new Vue({
-      template: '<div><test></test></div>',
-      components: {
-        test: resolve => {
-          setTimeout(() => {
-            resolve({
-              __esModule: true,
-              default: {
-                template: '<div>hi</div>'
-              }
-            })
-            // wait for parent update
-            Vue.nextTick(next)
-          }, 0)
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<!---->')
-    expect(vm.$children.length).toBe(0)
-
-    function next() {
-      expect(vm.$el.innerHTML).toBe('<div>hi</div>')
-      expect(vm.$children.length).toBe(1)
-      done()
-    }
-  })
-
-  it('as root', done => {
-    const vm = new Vue({
-      template: '<test></test>',
-      components: {
-        test: resolve => {
-          setTimeout(() => {
-            resolve({
-              template: '<div>hi</div>'
-            })
-            // wait for parent update
-            Vue.nextTick(next)
-          }, 0)
-        }
-      }
-    }).$mount()
-    expect(vm.$el.nodeType).toBe(8)
-    expect(vm.$children.length).toBe(0)
-
-    function next() {
-      expect(vm.$el.nodeType).toBe(1)
-      expect(vm.$el.outerHTML).toBe('<div>hi</div>')
-      expect(vm.$children.length).toBe(1)
-      done()
-    }
-  })
-
-  it('dynamic', done => {
-    const vm = new Vue({
-      template: '<component :is="view"></component>',
-      data: {
-        view: 'view-a'
-      },
-      components: {
-        'view-a': resolve => {
-          setTimeout(() => {
-            resolve({
-              template: '<div>A</div>'
-            })
-            Vue.nextTick(step1)
-          }, 0)
-        },
-        'view-b': resolve => {
-          setTimeout(() => {
-            resolve({
-              template: '<p>B</p>'
-            })
-            Vue.nextTick(step2)
-          }, 0)
-        }
-      }
-    }).$mount()
-    let aCalled = false
-    function step1() {
-      // ensure A is resolved only once
-      expect(aCalled).toBe(false)
-      aCalled = true
-      expect(vm.$el.tagName).toBe('DIV')
-      expect(vm.$el.textContent).toBe('A')
-      vm.view = 'view-b'
-    }
-
-    function step2() {
-      expect(vm.$el.tagName).toBe('P')
-      expect(vm.$el.textContent).toBe('B')
-      vm.view = 'view-a'
-
-      waitForUpdate(() => {
-        expect(vm.$el.tagName).toBe('DIV')
-        expect(vm.$el.textContent).toBe('A')
-      }).then(done)
-    }
-  })
-
-  it('warn reject', () => {
-    new Vue({
-      template: '<test></test>',
-      components: {
-        test: (resolve, reject) => {
-          reject('nooooo')
-        }
-      }
-    }).$mount()
-    expect('Reason: nooooo').toHaveBeenWarned()
-  })
-
-  it('with v-for', done => {
-    const vm = new Vue({
-      template: '<div><test v-for="n in list" :key="n" :n="n"></test></div>',
-      data: {
-        list: [1, 2, 3]
-      },
-      components: {
-        test: resolve => {
-          setTimeout(() => {
-            resolve({
-              props: ['n'],
-              template: '<div>{{n}}</div>'
-            })
-            Vue.nextTick(next)
-          }, 0)
-        }
-      }
-    }).$mount()
-
-    function next() {
-      expect(vm.$el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
-      done()
-    }
-  })
-
-  it('returning Promise', done => {
-    const vm = new Vue({
-      template: '<div><test></test></div>',
-      components: {
-        test: () => {
-          return new Promise(resolve => {
-            setTimeout(() => {
-              resolve({
-                template: '<div>hi</div>'
-              })
-              // wait for promise resolve and then parent update
-              Promise.resolve().then(() => {
-                Vue.nextTick(next)
-              })
-            }, 0)
-          })
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<!---->')
-    expect(vm.$children.length).toBe(0)
-
-    function next() {
-      expect(vm.$el.innerHTML).toBe('<div>hi</div>')
-      expect(vm.$children.length).toBe(1)
-      done()
-    }
-  })
-
-  describe('loading/error/timeout', () => {
-    it('with loading component', done => {
-      const vm = new Vue({
-        template: `<div><test/></div>`,
-        components: {
-          test: () => ({
-            component: new Promise(resolve => {
-              setTimeout(() => {
-                resolve({ template: '<div>hi</div>' })
-                // wait for promise resolve and then parent update
-                Promise.resolve().then(() => {
-                  Vue.nextTick(next)
-                })
-              }, 50)
-            }),
-            loading: { template: `<div>loading</div>` },
-            delay: 1
-          })
-        }
-      }).$mount()
-
-      expect(vm.$el.innerHTML).toBe('<!---->')
-
-      let loadingAsserted = false
-      setTimeout(() => {
-        Vue.nextTick(() => {
-          loadingAsserted = true
-          expect(vm.$el.textContent).toBe('loading')
-        })
-      }, 1)
-
-      function next() {
-        expect(loadingAsserted).toBe(true)
-        expect(vm.$el.textContent).toBe('hi')
-        done()
-      }
-    })
-
-    it('with loading component (0 delay)', done => {
-      const vm = new Vue({
-        template: `<div><test/></div>`,
-        components: {
-          test: () => ({
-            component: new Promise(resolve => {
-              setTimeout(() => {
-                resolve({ template: '<div>hi</div>' })
-                // wait for promise resolve and then parent update
-                Promise.resolve().then(() => {
-                  Vue.nextTick(next)
-                })
-              }, 50)
-            }),
-            loading: { template: `<div>loading</div>` },
-            delay: 0
-          })
-        }
-      }).$mount()
-
-      expect(vm.$el.textContent).toBe('loading')
-
-      function next() {
-        expect(vm.$el.textContent).toBe('hi')
-        done()
-      }
-    })
-
-    it('with error component', done => {
-      const vm = new Vue({
-        template: `<div><test/></div>`,
-        components: {
-          test: () => ({
-            component: new Promise((resolve, reject) => {
-              setTimeout(() => {
-                reject()
-                // wait for promise resolve and then parent update
-                Promise.resolve().then(() => {
-                  Vue.nextTick(next)
-                })
-              }, 50)
-            }),
-            loading: { template: `<div>loading</div>` },
-            error: { template: `<div>error</div>` },
-            delay: 0
-          })
-        }
-      }).$mount()
-
-      expect(vm.$el.textContent).toBe('loading')
-
-      function next() {
-        expect(`Failed to resolve async component`).toHaveBeenWarned()
-        expect(vm.$el.textContent).toBe('error')
-        done()
-      }
-    })
-
-    it('with error component + timeout', done => {
-      const vm = new Vue({
-        template: `<div><test/></div>`,
-        components: {
-          test: () => ({
-            component: new Promise((resolve, reject) => {
-              setTimeout(() => {
-                resolve({ template: '<div>hi</div>' })
-                // wait for promise resolve and then parent update
-                Promise.resolve().then(() => {
-                  Vue.nextTick(next)
-                })
-              }, 50)
-            }),
-            loading: { template: `<div>loading</div>` },
-            error: { template: `<div>error</div>` },
-            delay: 0,
-            timeout: 1
-          })
-        }
-      }).$mount()
-
-      expect(vm.$el.textContent).toBe('loading')
-
-      setTimeout(() => {
-        Vue.nextTick(() => {
-          expect(`Failed to resolve async component`).toHaveBeenWarned()
-          expect(vm.$el.textContent).toBe('error')
-        })
-      }, 1)
-
-      function next() {
-        expect(vm.$el.textContent).toBe('error') // late resolve ignored
-        done()
-      }
-    })
-
-    it('should not trigger timeout if resolved', done => {
-      const vm = new Vue({
-        template: `<div><test/></div>`,
-        components: {
-          test: () => ({
-            component: new Promise((resolve, reject) => {
-              setTimeout(() => {
-                resolve({ template: '<div>hi</div>' })
-              }, 10)
-            }),
-            error: { template: `<div>error</div>` },
-            timeout: 20
-          })
-        }
-      }).$mount()
-
-      setTimeout(() => {
-        expect(vm.$el.textContent).toBe('hi')
-        expect(`Failed to resolve async component`).not.toHaveBeenWarned()
-        done()
-      }, 50)
-    })
-
-    it('should not have running timeout/loading if resolved', done => {
-      const vm = new Vue({
-        template: `<div><test/></div>`,
-        components: {
-          test: () => ({
-            component: new Promise((resolve, reject) => {
-              setTimeout(() => {
-                resolve({ template: '<div>hi</div>' })
-                Promise.resolve().then(() => {
-                  Vue.nextTick(next)
-                })
-              }, 10)
-            }),
-            loading: { template: `<div>loading</div>` },
-            delay: 30,
-            error: { template: `<div>error</div>` },
-            timeout: 40
-          })
-        }
-      }).$mount()
-
-      function next() {
-        expect(vm.$el.textContent).toBe('hi')
-        // the afterEach() will ensure that the timeouts for delay and timeout have been cleared
-        done()
-      }
-    })
-
-    // #7107
-    it(`should work when resolving sync in sibling component's mounted hook`, done => {
-      let resolveTwo
-
-      const vm = new Vue({
-        template: `<div><one/> <two/></div>`,
-        components: {
-          one: {
-            template: `<div>one</div>`,
-            mounted() {
-              resolveTwo()
-            }
-          },
-          two: resolve => {
-            resolveTwo = () => {
-              resolve({
-                template: `<div>two</div>`
-              })
-            }
-          }
-        }
-      }).$mount()
-
-      expect(vm.$el.textContent).toBe('one ')
-
-      waitForUpdate(() => {
-        expect(vm.$el.textContent).toBe('one two')
-      }).then(done)
-    })
-  })
-})
diff --git a/test/unit/features/component/component-keep-alive.spec.ts b/test/unit/features/component/component-keep-alive.spec.ts
deleted file mode 100644
index 28722d0c6a6..00000000000
--- a/test/unit/features/component/component-keep-alive.spec.ts
+++ /dev/null
@@ -1,833 +0,0 @@
-import Vue from 'vue'
-
-describe('Component keep-alive', () => {
-  let components, one, two, el
-  beforeEach(() => {
-    one = {
-      template: '<div>one</div>',
-      created: vi.fn(),
-      mounted: vi.fn(),
-      activated: vi.fn(),
-      deactivated: vi.fn(),
-      destroyed: vi.fn()
-    }
-    two = {
-      template: '<div>two</div>',
-      created: vi.fn(),
-      mounted: vi.fn(),
-      activated: vi.fn(),
-      deactivated: vi.fn(),
-      destroyed: vi.fn()
-    }
-    components = {
-      one,
-      two
-    }
-    el = document.createElement('div')
-    document.body.appendChild(el)
-  })
-
-  function assertHookCalls(component, callCounts) {
-    expect([
-      component.created.mock.calls.length,
-      component.mounted.mock.calls.length,
-      component.activated.mock.calls.length,
-      component.deactivated.mock.calls.length,
-      component.destroyed.mock.calls.length
-    ]).toEqual(callCounts)
-  }
-
-  it('should work', done => {
-    const vm = new Vue({
-      template: `
-        <div v-if="ok">
-          <keep-alive>
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        ok: true
-      },
-      components
-    }).$mount()
-    expect(vm.$el.textContent).toBe('one')
-    assertHookCalls(one, [1, 1, 1, 0, 0])
-    assertHookCalls(two, [0, 0, 0, 0, 0])
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('two')
-      assertHookCalls(one, [1, 1, 1, 1, 0])
-      assertHookCalls(two, [1, 1, 1, 0, 0])
-      vm.view = 'one'
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('one')
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 1, 1, 0])
-        vm.view = 'two'
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('two')
-        assertHookCalls(one, [1, 1, 2, 2, 0])
-        assertHookCalls(two, [1, 1, 2, 1, 0])
-        vm.ok = false // teardown
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('')
-        assertHookCalls(one, [1, 1, 2, 2, 1])
-        assertHookCalls(two, [1, 1, 2, 2, 1])
-      })
-      .then(done)
-  })
-
-  it('should invoke hooks on the entire sub tree', done => {
-    one.template = '<two/>'
-    one.components = { two }
-
-    const vm = new Vue({
-      template: `
-        <div>
-          <keep-alive>
-            <one v-if="ok"/>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        ok: true
-      },
-      components
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('two')
-    assertHookCalls(one, [1, 1, 1, 0, 0])
-    assertHookCalls(two, [1, 1, 1, 0, 0])
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('')
-      assertHookCalls(one, [1, 1, 1, 1, 0])
-      assertHookCalls(two, [1, 1, 1, 1, 0])
-      vm.ok = true
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('two')
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 2, 1, 0])
-        vm.ok = false
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('')
-        assertHookCalls(one, [1, 1, 2, 2, 0])
-        assertHookCalls(two, [1, 1, 2, 2, 0])
-      })
-      .then(done)
-  })
-
-  it('should handle nested keep-alive hooks properly', done => {
-    one.template = '<keep-alive><two v-if="ok" /></keep-alive>'
-    one.data = () => ({ ok: true })
-    one.components = { two }
-
-    const vm = new Vue({
-      template: `
-        <div>
-          <keep-alive>
-            <one v-if="ok" ref="one" />
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        ok: true
-      },
-      components
-    }).$mount()
-
-    const oneInstance = vm.$refs.one
-    expect(vm.$el.textContent).toBe('two')
-    assertHookCalls(one, [1, 1, 1, 0, 0])
-    assertHookCalls(two, [1, 1, 1, 0, 0])
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('')
-      assertHookCalls(one, [1, 1, 1, 1, 0])
-      assertHookCalls(two, [1, 1, 1, 1, 0])
-    })
-      .then(() => {
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('two')
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 2, 1, 0])
-      })
-      .then(() => {
-        // toggle sub component when activated
-        oneInstance.ok = false
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('')
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 2, 2, 0])
-      })
-      .then(() => {
-        oneInstance.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('two')
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 3, 2, 0])
-      })
-      .then(() => {
-        vm.ok = false
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('')
-        assertHookCalls(one, [1, 1, 2, 2, 0])
-        assertHookCalls(two, [1, 1, 3, 3, 0])
-      })
-      .then(() => {
-        // toggle sub component when parent is deactivated
-        oneInstance.ok = false
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('')
-        assertHookCalls(one, [1, 1, 2, 2, 0])
-        assertHookCalls(two, [1, 1, 3, 3, 0]) // should not be affected
-      })
-      .then(() => {
-        oneInstance.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('')
-        assertHookCalls(one, [1, 1, 2, 2, 0])
-        assertHookCalls(two, [1, 1, 3, 3, 0]) // should not be affected
-      })
-      .then(() => {
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('two')
-        assertHookCalls(one, [1, 1, 3, 2, 0])
-        assertHookCalls(two, [1, 1, 4, 3, 0])
-      })
-      .then(() => {
-        oneInstance.ok = false
-        vm.ok = false
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('')
-        assertHookCalls(one, [1, 1, 3, 3, 0])
-        assertHookCalls(two, [1, 1, 4, 4, 0])
-      })
-      .then(() => {
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('')
-        assertHookCalls(one, [1, 1, 4, 3, 0])
-        assertHookCalls(two, [1, 1, 4, 4, 0]) // should remain inactive
-      })
-      .then(done)
-  })
-
-  function sharedAssertions(vm, done) {
-    expect(vm.$el.textContent).toBe('one')
-    assertHookCalls(one, [1, 1, 1, 0, 0])
-    assertHookCalls(two, [0, 0, 0, 0, 0])
-    vm.view = 'two'
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('two')
-      assertHookCalls(one, [1, 1, 1, 1, 0])
-      assertHookCalls(two, [1, 1, 0, 0, 0])
-      vm.view = 'one'
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('one')
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        assertHookCalls(two, [1, 1, 0, 0, 1])
-        vm.view = 'two'
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('two')
-        assertHookCalls(one, [1, 1, 2, 2, 0])
-        assertHookCalls(two, [2, 2, 0, 0, 1])
-        vm.ok = false // teardown
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('')
-        assertHookCalls(one, [1, 1, 2, 2, 1])
-        assertHookCalls(two, [2, 2, 0, 0, 2])
-      })
-      .then(done)
-  }
-
-  it('include (string)', done => {
-    const vm = new Vue({
-      template: `
-        <div v-if="ok">
-          <keep-alive include="one">
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        ok: true
-      },
-      components
-    }).$mount()
-    sharedAssertions(vm, done)
-  })
-
-  it('include (regex)', done => {
-    const vm = new Vue({
-      template: `
-        <div v-if="ok">
-          <keep-alive :include="/^one$/">
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        ok: true
-      },
-      components
-    }).$mount()
-    sharedAssertions(vm, done)
-  })
-
-  it('include (array)', done => {
-    const vm = new Vue({
-      template: `
-        <div v-if="ok">
-          <keep-alive :include="['one']">
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        ok: true
-      },
-      components
-    }).$mount()
-    sharedAssertions(vm, done)
-  })
-
-  it('exclude (string)', done => {
-    const vm = new Vue({
-      template: `
-        <div v-if="ok">
-          <keep-alive exclude="two">
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        ok: true
-      },
-      components
-    }).$mount()
-    sharedAssertions(vm, done)
-  })
-
-  it('exclude (regex)', done => {
-    const vm = new Vue({
-      template: `
-        <div v-if="ok">
-          <keep-alive :exclude="/^two$/">
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        ok: true
-      },
-      components
-    }).$mount()
-    sharedAssertions(vm, done)
-  })
-
-  it('exclude (array)', done => {
-    const vm = new Vue({
-      template: `
-        <div v-if="ok">
-          <keep-alive :exclude="['two']">
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        ok: true
-      },
-      components
-    }).$mount()
-    sharedAssertions(vm, done)
-  })
-
-  it('include + exclude', done => {
-    const vm = new Vue({
-      template: `
-        <div v-if="ok">
-          <keep-alive include="one,two" exclude="two">
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        ok: true
-      },
-      components
-    }).$mount()
-    sharedAssertions(vm, done)
-  })
-
-  it('prune cache on include/exclude change', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <keep-alive :include="include">
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        include: 'one,two'
-      },
-      components
-    }).$mount()
-
-    vm.view = 'two'
-    waitForUpdate(() => {
-      assertHookCalls(one, [1, 1, 1, 1, 0])
-      assertHookCalls(two, [1, 1, 1, 0, 0])
-      vm.include = 'two'
-    })
-      .then(() => {
-        assertHookCalls(one, [1, 1, 1, 1, 1])
-        assertHookCalls(two, [1, 1, 1, 0, 0])
-        vm.view = 'one'
-      })
-      .then(() => {
-        assertHookCalls(one, [2, 2, 1, 1, 1])
-        assertHookCalls(two, [1, 1, 1, 1, 0])
-      })
-      .then(done)
-  })
-
-  it('prune cache on include/exclude change + view switch', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <keep-alive :include="include">
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        include: 'one,two'
-      },
-      components
-    }).$mount()
-
-    vm.view = 'two'
-    waitForUpdate(() => {
-      assertHookCalls(one, [1, 1, 1, 1, 0])
-      assertHookCalls(two, [1, 1, 1, 0, 0])
-      vm.include = 'one'
-      vm.view = 'one'
-    })
-      .then(() => {
-        assertHookCalls(one, [1, 1, 2, 1, 0])
-        // two should be pruned
-        assertHookCalls(two, [1, 1, 1, 1, 1])
-      })
-      .then(done)
-  })
-
-  it('should not prune currently active instance', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <keep-alive :include="include">
-            <component :is="view"></component>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        view: 'one',
-        include: 'one,two'
-      },
-      components
-    }).$mount()
-
-    vm.include = 'two'
-    waitForUpdate(() => {
-      assertHookCalls(one, [1, 1, 1, 0, 0])
-      assertHookCalls(two, [0, 0, 0, 0, 0])
-      vm.view = 'two'
-    })
-      .then(() => {
-        assertHookCalls(one, [1, 1, 1, 0, 1])
-        assertHookCalls(two, [1, 1, 1, 0, 0])
-      })
-      .then(done)
-  })
-
-  // #3882
-  it('deeply nested keep-alive should be destroyed properly', done => {
-    one.template = `<div><keep-alive><two></two></keep-alive></div>`
-    one.components = { two }
-    const vm = new Vue({
-      template: `<div><parent v-if="ok"></parent></div>`,
-      data: { ok: true },
-      components: {
-        parent: {
-          template: `<div><keep-alive><one></one></keep-alive></div>`,
-          components: { one }
-        }
-      }
-    }).$mount()
-
-    assertHookCalls(one, [1, 1, 1, 0, 0])
-    assertHookCalls(two, [1, 1, 1, 0, 0])
-
-    vm.ok = false
-    waitForUpdate(() => {
-      assertHookCalls(one, [1, 1, 1, 1, 1])
-      assertHookCalls(two, [1, 1, 1, 1, 1])
-    }).then(done)
-  })
-
-  // #4237
-  it('should update latest props/listeners for a re-activated component', done => {
-    const one = {
-      props: ['prop'],
-      template: `<div>one {{ prop }}</div>`
-    }
-    const two = {
-      props: ['prop'],
-      template: `<div>two {{ prop }}</div>`
-    }
-    const vm = new Vue({
-      data: { view: 'one', n: 1 },
-      template: `
-        <div>
-          <keep-alive>
-            <component :is="view" :prop="n"></component>
-          </keep-alive>
-        </div>
-      `,
-      components: { one, two }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('one 1')
-    vm.n++
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('one 2')
-      vm.view = 'two'
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('two 2')
-      })
-      .then(done)
-  })
-
-  it('max', done => {
-    const spyA = vi.fn()
-    const spyB = vi.fn()
-    const spyC = vi.fn()
-    const spyAD = vi.fn()
-    const spyBD = vi.fn()
-    const spyCD = vi.fn()
-
-    function assertCount(calls) {
-      expect([
-        spyA.mock.calls.length,
-        spyAD.mock.calls.length,
-        spyB.mock.calls.length,
-        spyBD.mock.calls.length,
-        spyC.mock.calls.length,
-        spyCD.mock.calls.length
-      ]).toEqual(calls)
-    }
-
-    const vm = new Vue({
-      template: `
-        <keep-alive max="2">
-          <component :is="n"></component>
-        </keep-alive>
-      `,
-      data: {
-        n: 'aa'
-      },
-      components: {
-        aa: {
-          template: '<div>a</div>',
-          created: spyA,
-          destroyed: spyAD
-        },
-        bb: {
-          template: '<div>bbb</div>',
-          created: spyB,
-          destroyed: spyBD
-        },
-        cc: {
-          template: '<div>ccc</div>',
-          created: spyC,
-          destroyed: spyCD
-        }
-      }
-    }).$mount()
-
-    assertCount([1, 0, 0, 0, 0, 0])
-    vm.n = 'bb'
-    waitForUpdate(() => {
-      assertCount([1, 0, 1, 0, 0, 0])
-      vm.n = 'cc'
-    })
-      .then(() => {
-        // should prune A because max cache reached
-        assertCount([1, 1, 1, 0, 1, 0])
-        vm.n = 'bb'
-      })
-      .then(() => {
-        // B should be reused, and made latest
-        assertCount([1, 1, 1, 0, 1, 0])
-        vm.n = 'aa'
-      })
-      .then(() => {
-        // C should be pruned because B was used last so C is the oldest cached
-        assertCount([2, 1, 1, 0, 1, 1])
-      })
-      .then(done)
-  })
-
-  it('max=1', done => {
-    const spyA = vi.fn()
-    const spyB = vi.fn()
-    const spyC = vi.fn()
-    const spyAD = vi.fn()
-    const spyBD = vi.fn()
-    const spyCD = vi.fn()
-
-    function assertCount(calls) {
-      expect([
-        spyA.mock.calls.length,
-        spyAD.mock.calls.length,
-        spyB.mock.calls.length,
-        spyBD.mock.calls.length,
-        spyC.mock.calls.length,
-        spyCD.mock.calls.length
-      ]).toEqual(calls)
-    }
-
-    const vm = new Vue({
-      template: `
-        <keep-alive max="1">
-          <component :is="n"></component>
-        </keep-alive>
-      `,
-      data: {
-        n: 'aa'
-      },
-      components: {
-        aa: {
-          template: '<div>a</div>',
-          created: spyA,
-          destroyed: spyAD
-        },
-        bb: {
-          template: '<div>bbb</div>',
-          created: spyB,
-          destroyed: spyBD
-        },
-        cc: {
-          template: '<div>ccc</div>',
-          created: spyC,
-          destroyed: spyCD
-        }
-      }
-    }).$mount()
-
-    assertCount([1, 0, 0, 0, 0, 0])
-    vm.n = 'bb'
-    waitForUpdate(() => {
-      // should prune A because max cache reached
-      assertCount([1, 1, 1, 0, 0, 0])
-      vm.n = 'cc'
-    })
-      .then(() => {
-        // should prune B because max cache reached
-        assertCount([1, 1, 1, 1, 1, 0])
-        vm.n = 'bb'
-      })
-      .then(() => {
-        // B is recreated
-        assertCount([1, 1, 2, 1, 1, 1])
-        vm.n = 'aa'
-      })
-      .then(() => {
-        // B is destroyed and A recreated
-        assertCount([2, 1, 2, 2, 1, 1])
-      })
-      .then(done)
-  })
-
-  it('should warn unknown component inside', () => {
-    new Vue({
-      template: `<keep-alive><foo/></keep-alive>`
-    }).$mount()
-    expect(`Unknown custom element: <foo>`).toHaveBeenWarned()
-  })
-
-  // #6938
-  it('should not cache anonymous component when include is specified', done => {
-    const Foo = {
-      name: 'foo',
-      template: `<div>foo</div>`,
-      created: vi.fn()
-    }
-
-    const Bar = {
-      template: `<div>bar</div>`,
-      created: vi.fn()
-    }
-
-    const Child = {
-      functional: true,
-      render(h, ctx) {
-        return h(ctx.props.view ? Foo : Bar)
-      }
-    }
-
-    const vm = new Vue({
-      template: `
-        <keep-alive include="foo">
-          <child :view="view"></child>
-        </keep-alive>
-      `,
-      data: {
-        view: true
-      },
-      components: { Child }
-    }).$mount()
-
-    function assert(foo, bar) {
-      expect(Foo.created.mock.calls.length).toBe(foo)
-      expect(Bar.created.mock.calls.length).toBe(bar)
-    }
-
-    expect(vm.$el.textContent).toBe('foo')
-    assert(1, 0)
-    vm.view = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('bar')
-      assert(1, 1)
-      vm.view = true
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('foo')
-        assert(1, 1)
-        vm.view = false
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('bar')
-        assert(1, 2)
-      })
-      .then(done)
-  })
-
-  it('should cache anonymous components if include is not specified', done => {
-    const Foo = {
-      template: `<div>foo</div>`,
-      created: vi.fn()
-    }
-
-    const Bar = {
-      template: `<div>bar</div>`,
-      created: vi.fn()
-    }
-
-    const Child = {
-      functional: true,
-      render(h, ctx) {
-        return h(ctx.props.view ? Foo : Bar)
-      }
-    }
-
-    const vm = new Vue({
-      template: `
-        <keep-alive>
-          <child :view="view"></child>
-        </keep-alive>
-      `,
-      data: {
-        view: true
-      },
-      components: { Child }
-    }).$mount()
-
-    function assert(foo, bar) {
-      expect(Foo.created.mock.calls.length).toBe(foo)
-      expect(Bar.created.mock.calls.length).toBe(bar)
-    }
-
-    expect(vm.$el.textContent).toBe('foo')
-    assert(1, 0)
-    vm.view = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('bar')
-      assert(1, 1)
-      vm.view = true
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('foo')
-        assert(1, 1)
-        vm.view = false
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('bar')
-        assert(1, 1)
-      })
-      .then(done)
-  })
-
-  // #7105
-  it('should not destroy active instance when pruning cache', done => {
-    const Foo = {
-      template: `<div>foo</div>`,
-      destroyed: vi.fn()
-    }
-    const vm = new Vue({
-      template: `
-        <div>
-          <keep-alive :include="include">
-            <foo/>
-          </keep-alive>
-        </div>
-      `,
-      data: {
-        include: ['foo']
-      },
-      components: { Foo }
-    }).$mount()
-    // condition: a render where a previous component is reused
-    vm.include = ['foo']
-    waitForUpdate(() => {
-      vm.include = ['']
-    })
-      .then(() => {
-        expect(Foo.destroyed).not.toHaveBeenCalled()
-      })
-      .then(done)
-  })
-})
diff --git a/test/unit/features/component/component-scoped-slot.spec.ts b/test/unit/features/component/component-scoped-slot.spec.ts
deleted file mode 100644
index 96c23f8c66c..00000000000
--- a/test/unit/features/component/component-scoped-slot.spec.ts
+++ /dev/null
@@ -1,1406 +0,0 @@
-import Vue from 'vue'
-
-describe('Component scoped slot', () => {
-  it('default slot', done => {
-    const vm = new Vue({
-      template: `
-        <test ref="test">
-          <template slot-scope="props">
-            <span>{{ props.msg }}</span>
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return { msg: 'hello' }
-          },
-          template: `
-            <div>
-              <slot :msg="msg"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-    vm.$refs.test.msg = 'world'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<span>world</span>')
-    }).then(done)
-  })
-
-  it('default slot (plain element)', done => {
-    const vm = new Vue({
-      template: `
-        <test ref="test">
-          <span slot-scope="props">{{ props.msg }}</span>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return { msg: 'hello' }
-          },
-          template: `
-            <div>
-              <slot :msg="msg"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-    vm.$refs.test.msg = 'world'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<span>world</span>')
-    }).then(done)
-  })
-
-  it('with v-bind', done => {
-    const vm = new Vue({
-      template: `
-        <test ref="test">
-          <template slot-scope="props">
-            <span>{{ props.msg }} {{ props.msg2 }} {{ props.msg3 }}</span>
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return {
-              msg: 'hello',
-              obj: { msg2: 'world', msg3: '.' }
-            }
-          },
-          template: `
-            <div>
-              <slot :msg="msg" v-bind="obj" msg3="!"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe('<span>hello world !</span>')
-    vm.$refs.test.msg = 'bye'
-    vm.$refs.test.obj.msg2 = 'bye'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<span>bye bye !</span>')
-    }).then(done)
-  })
-
-  it('should warn when using v-bind with no object', () => {
-    new Vue({
-      template: `
-        <test ref="test">
-          <template scope="props">
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return {
-              text: 'some text'
-            }
-          },
-          template: `
-            <div>
-              <slot v-bind="text"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-    expect('slot v-bind without argument expects an Object').toHaveBeenWarned()
-  })
-
-  it('should not warn when using v-bind with object', () => {
-    new Vue({
-      template: `
-        <test ref="test">
-          <template scope="props">
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return {
-              foo: {
-                text: 'some text'
-              }
-            }
-          },
-          template: `
-            <div>
-              <slot v-bind="foo"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-    expect(
-      'slot v-bind without argument expects an Object'
-    ).not.toHaveBeenWarned()
-  })
-
-  it('named scoped slot', done => {
-    const vm = new Vue({
-      template: `
-        <test ref="test">
-          <template slot="item" slot-scope="props">
-            <span>{{ props.foo }}</span><span>{{ props.bar }}</span>
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return { foo: 'FOO', bar: 'BAR' }
-          },
-          template: `
-            <div>
-              <slot name="item" :foo="foo" :bar="bar"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe('<span>FOO</span><span>BAR</span>')
-    vm.$refs.test.foo = 'BAZ'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<span>BAZ</span><span>BAR</span>')
-    }).then(done)
-  })
-
-  it('named scoped slot (plain element)', done => {
-    const vm = new Vue({
-      template: `
-        <test ref="test">
-          <span slot="item" slot-scope="props">{{ props.foo }} {{ props.bar }}</span>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return { foo: 'FOO', bar: 'BAR' }
-          },
-          template: `
-            <div>
-              <slot name="item" :foo="foo" :bar="bar"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe('<span>FOO BAR</span>')
-    vm.$refs.test.foo = 'BAZ'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<span>BAZ BAR</span>')
-    }).then(done)
-  })
-
-  it('fallback content', () => {
-    const vm = new Vue({
-      template: `<test></test>`,
-      components: {
-        test: {
-          data() {
-            return { msg: 'hello' }
-          },
-          template: `
-            <div>
-              <slot name="item" :text="msg">
-                <span>{{ msg }} fallback</span>
-              </slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>hello fallback</span>')
-  })
-
-  it('slot with v-for', done => {
-    const vm = new Vue({
-      template: `
-        <test ref="test">
-          <template slot="item" slot-scope="props">
-            <span>{{ props.text }}</span>
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return {
-              items: ['foo', 'bar', 'baz']
-            }
-          },
-          template: `
-            <div>
-              <slot v-for="item in items" name="item" :text="item"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-
-    function assertOutput() {
-      expect(vm.$el.innerHTML).toBe(
-        vm.$refs.test.items
-          .map(item => {
-            return `<span>${item}</span>`
-          })
-          .join('')
-      )
-    }
-
-    assertOutput()
-    vm.$refs.test.items.reverse()
-    waitForUpdate(assertOutput)
-      .then(() => {
-        vm.$refs.test.items.push('qux')
-      })
-      .then(assertOutput)
-      .then(done)
-  })
-
-  it('slot inside v-for', done => {
-    const vm = new Vue({
-      template: `
-        <test ref="test">
-          <template slot="item" slot-scope="props">
-            <span>{{ props.text }}</span>
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return {
-              items: ['foo', 'bar', 'baz']
-            }
-          },
-          template: `
-            <ul>
-              <li v-for="item in items">
-                <slot name="item" :text="item"></slot>
-              </li>
-            </ul>
-          `
-        }
-      }
-    }).$mount()
-
-    function assertOutput() {
-      expect(vm.$el.innerHTML).toBe(
-        vm.$refs.test.items
-          .map(item => {
-            return `<li><span>${item}</span></li>`
-          })
-          .join('')
-      )
-    }
-
-    assertOutput()
-    vm.$refs.test.items.reverse()
-    waitForUpdate(assertOutput)
-      .then(() => {
-        vm.$refs.test.items.push('qux')
-      })
-      .then(assertOutput)
-      .then(done)
-  })
-
-  it('scoped slot without scope alias', () => {
-    const vm = new Vue({
-      template: `
-        <test ref="test">
-          <span slot="item">I am static</span>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return { msg: 'hello' }
-          },
-          template: `
-            <div>
-              <slot name="item" :text="msg"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>I am static</span>')
-  })
-
-  it('non-scoped slot with scope alias', () => {
-    const vm = new Vue({
-      template: `
-        <test ref="test">
-          <template slot="item" slot-scope="props">
-            <span>{{ props.text || 'meh' }}</span>
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return { msg: 'hello' }
-          },
-          template: `
-            <div>
-              <slot name="item"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>meh</span>')
-  })
-
-  it('warn key on slot', () => {
-    new Vue({
-      template: `
-        <test ref="test">
-          <template slot="item" slot-scope="props">
-            <span>{{ props.text }}</span>
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return {
-              items: ['foo', 'bar', 'baz']
-            }
-          },
-          template: `
-            <div>
-              <slot v-for="item in items" name="item" :text="item" :key="item"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-    expect(`\`key\` does not work on <slot>`).toHaveBeenWarned()
-  })
-
-  it('render function usage (named, via data)', done => {
-    const vm = new Vue({
-      render(h) {
-        return h('test', {
-          ref: 'test',
-          scopedSlots: {
-            item: props => h('span', props.text)
-          }
-        })
-      },
-      components: {
-        test: {
-          data() {
-            return { msg: 'hello' }
-          },
-          render(h) {
-            return h(
-              'div',
-              this.$scopedSlots.item({
-                text: this.msg
-              })
-            )
-          }
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-    vm.$refs.test.msg = 'world'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<span>world</span>')
-    }).then(done)
-  })
-
-  it('render function usage (default, as children)', () => {
-    const vm = new Vue({
-      render(h) {
-        return h('test', [props => h('span', [props.msg])])
-      },
-      components: {
-        test: {
-          data() {
-            return { msg: 'hello' }
-          },
-          render(h) {
-            return h('div', this.$scopedSlots.default({ msg: this.msg }))
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-  })
-
-  it('render function usage (default, as root)', () => {
-    const vm = new Vue({
-      render(h) {
-        return h('test', [props => h('span', [props.msg])])
-      },
-      components: {
-        test: {
-          data() {
-            return { msg: 'hello' }
-          },
-          render(h) {
-            const res = this.$scopedSlots.default({ msg: this.msg })
-            // all scoped slots should be normalized into arrays
-            expect(Array.isArray(res)).toBe(true)
-            return res
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.outerHTML).toBe('<span>hello</span>')
-  })
-
-  // new in 2.6, unifying all slots as functions
-  it('non-scoped slots should also be available on $scopedSlots', () => {
-    const vm = new Vue({
-      template: `<foo>before <div slot="bar" slot-scope="scope">{{ scope.msg }}</div> after</foo>`,
-      components: {
-        foo: {
-          render(h) {
-            return h('div', [
-              this.$scopedSlots.default(),
-              this.$scopedSlots.bar({ msg: 'hi' })
-            ])
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(`before  after<div>hi</div>`)
-  })
-
-  // #4779
-  it('should support dynamic slot target', done => {
-    const Child = {
-      template: `
-        <div>
-          <slot name="a" msg="a" />
-          <slot name="b" msg="b" />
-        </div>
-      `
-    }
-
-    const vm = new Vue({
-      data: {
-        a: 'a',
-        b: 'b'
-      },
-      template: `
-        <child>
-          <template :slot="a" slot-scope="props">A {{ props.msg }}</template>
-          <template :slot="b" slot-scope="props">B {{ props.msg }}</template>
-        </child>
-      `,
-      components: { Child }
-    }).$mount()
-
-    expect(vm.$el.textContent.trim()).toBe('A a B b')
-
-    // switch slots
-    vm.a = 'b'
-    vm.b = 'a'
-    waitForUpdate(() => {
-      expect(vm.$el.textContent.trim()).toBe('B a A b')
-    }).then(done)
-  })
-
-  // it('render function usage (JSX)', () => {
-  //   const vm = new Vue({
-  //     render (h) {
-  //       return (<test>{
-  //         props => <span>{props.msg}</span>
-  //       }</test>)
-  //     },
-  //     components: {
-  //       test: {
-  //         data () {
-  //           return { msg: 'hello' }
-  //         },
-  //         render (h) {
-  //           return <div>
-  //             {this.$scopedSlots.default({ msg: this.msg })}
-  //           </div>
-  //         }
-  //       }
-  //     }
-  //   }).$mount()
-  //   expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-  // })
-
-  // #5615
-  it('scoped slot with v-for', done => {
-    const vm = new Vue({
-      data: { names: ['foo', 'bar'] },
-      template: `
-        <test ref="test">
-          <template v-for="n in names" :slot="n" slot-scope="props">
-            <span>{{ props.msg }}</span>
-          </template>
-          <template slot="abc" slot-scope="props">
-            <span>{{ props.msg }}</span>
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data: () => ({ msg: 'hello' }),
-          template: `
-            <div>
-              <slot name="foo" :msg="msg + ' foo'"></slot>
-              <slot name="bar" :msg="msg + ' bar'"></slot>
-              <slot name="abc" :msg="msg + ' abc'"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe(
-      '<span>hello foo</span> <span>hello bar</span> <span>hello abc</span>'
-    )
-    vm.$refs.test.msg = 'world'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>world foo</span> <span>world bar</span> <span>world abc</span>'
-      )
-    }).then(done)
-  })
-
-  it('scoped slot with v-for (plain elements)', done => {
-    const vm = new Vue({
-      data: { names: ['foo', 'bar'] },
-      template: `
-        <test ref="test">
-          <span v-for="n in names" :slot="n" slot-scope="props">{{ props.msg }}</span>
-          <span slot="abc" slot-scope="props">{{ props.msg }}</span>
-        </test>
-      `,
-      components: {
-        test: {
-          data: () => ({ msg: 'hello' }),
-          template: `
-            <div>
-              <slot name="foo" :msg="msg + ' foo'"></slot>
-              <slot name="bar" :msg="msg + ' bar'"></slot>
-              <slot name="abc" :msg="msg + ' abc'"></slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe(
-      '<span>hello foo</span> <span>hello bar</span> <span>hello abc</span>'
-    )
-    vm.$refs.test.msg = 'world'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>world foo</span> <span>world bar</span> <span>world abc</span>'
-      )
-    }).then(done)
-  })
-
-  // #6725
-  it('scoped slot with v-if', done => {
-    const vm = new Vue({
-      data: {
-        ok: false
-      },
-      template: `
-        <test>
-          <template v-if="ok" slot-scope="foo">
-            <p>{{ foo.text }}</p>
-          </template>
-        </test>
-      `,
-      components: {
-        test: {
-          data() {
-            return { msg: 'hello' }
-          },
-          template: `
-            <div>
-              <slot :text="msg">
-                <span>{{ msg }} fallback</span>
-              </slot>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>hello fallback</span>')
-
-    vm.ok = true
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<p>hello</p>')
-    }).then(done)
-  })
-
-  // #9422
-  // the behavior of the new syntax is slightly different.
-  it('scoped slot v-if using slot-scope value', () => {
-    const Child = {
-      template: '<div><slot value="foo"/></div>'
-    }
-    const vm = new Vue({
-      components: { Child },
-      template: `
-        <child>
-          <template slot-scope="{ value }" v-if="value">
-            foo {{ value }}
-          </template>
-        </child>
-      `
-    }).$mount()
-    expect(vm.$el.textContent).toMatch(`foo foo`)
-  })
-
-  // 2.6 new slot syntax
-  describe('v-slot syntax', () => {
-    const Foo = {
-      render(h) {
-        return h('div', [
-          this.$scopedSlots.default &&
-            this.$scopedSlots.default('from foo default'),
-          this.$scopedSlots.one && this.$scopedSlots.one('from foo one'),
-          this.$scopedSlots.two && this.$scopedSlots.two('from foo two')
-        ])
-      }
-    }
-
-    const Bar = {
-      render(h) {
-        return (
-          this.$scopedSlots.default && this.$scopedSlots.default('from bar')
-        )
-      }
-    }
-
-    const Baz = {
-      render(h) {
-        return (
-          this.$scopedSlots.default && this.$scopedSlots.default('from baz')
-        )
-      }
-    }
-
-    const toNamed = (syntax, name) =>
-      syntax[0] === '#'
-        ? `#${name}` // shorthand
-        : `${syntax}:${name}` // full syntax
-
-    function runSuite(syntax) {
-      it('default slot', () => {
-        const vm = new Vue({
-          template: `<foo ${syntax}="foo">{{ foo }}<div>{{ foo }}</div></foo>`,
-          components: { Foo }
-        }).$mount()
-        expect(vm.$el.innerHTML).toBe(
-          `from foo default<div>from foo default</div>`
-        )
-      })
-
-      it('nested default slots', () => {
-        const vm = new Vue({
-          template: `
-            <foo ${syntax}="foo">
-              <bar ${syntax}="bar">
-                <baz ${syntax}="baz">
-                  {{ foo }} | {{ bar }} | {{ baz }}
-                </baz>
-              </bar>
-            </foo>
-          `,
-          components: { Foo, Bar, Baz }
-        }).$mount()
-        expect(vm.$el.innerHTML.trim()).toBe(
-          `from foo default | from bar | from baz`
-        )
-      })
-
-      it('named slots', () => {
-        const vm = new Vue({
-          template: `
-            <foo>
-              <template ${toNamed(syntax, 'default')}="foo">
-                {{ foo }}
-              </template>
-              <template ${toNamed(syntax, 'one')}="one">
-                {{ one }}
-              </template>
-              <template ${toNamed(syntax, 'two')}="two">
-                {{ two }}
-              </template>
-            </foo>
-          `,
-          components: { Foo }
-        }).$mount()
-        expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(
-          `from foo default from foo one from foo two`
-        )
-      })
-
-      it('nested + named + default slots', () => {
-        const vm = new Vue({
-          template: `
-            <foo>
-              <template ${toNamed(syntax, 'one')}="one">
-                <bar ${syntax}="bar">
-                  {{ one }} {{ bar }}
-                </bar>
-              </template>
-              <template ${toNamed(syntax, 'two')}="two">
-                <baz ${syntax}="baz">
-                  {{ two }} {{ baz }}
-                </baz>
-              </template>
-            </foo>
-          `,
-          components: { Foo, Bar, Baz }
-        }).$mount()
-        expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(
-          `from foo one from bar from foo two from baz`
-        )
-      })
-
-      it('should warn v-slot usage on non-component elements', () => {
-        new Vue({
-          template: `<div ${syntax}="foo"/>`
-        }).$mount()
-        expect(
-          `v-slot can only be used on components or <template>`
-        ).toHaveBeenWarned()
-      })
-
-      it('should warn mixed usage', () => {
-        new Vue({
-          template: `<foo><bar slot="one" slot-scope="bar" ${syntax}="bar"></bar></foo>`,
-          components: { Foo, Bar }
-        }).$mount()
-        expect(
-          `Unexpected mixed usage of different slot syntaxes`
-        ).toHaveBeenWarned()
-      })
-
-      it('should warn invalid parameter expression', () => {
-        new Vue({
-          template: `<foo ${syntax}="1"></foo>`,
-          components: { Foo }
-        }).$mount()
-        expect('invalid function parameter expression').toHaveBeenWarned()
-      })
-
-      it('should allow destructuring props with default value', () => {
-        new Vue({
-          template: `<foo ${syntax}="{ foo = { bar: '1' } }"></foo>`,
-          components: { Foo }
-        }).$mount()
-        expect('invalid function parameter expression').not.toHaveBeenWarned()
-      })
-    }
-
-    // run tests for both full syntax and shorthand
-    runSuite('v-slot')
-    runSuite('#default')
-
-    it('shorthand named slots', () => {
-      const vm = new Vue({
-        template: `
-          <foo>
-            <template #default="foo">
-              {{ foo }}
-            </template>
-            <template #one="one">
-              {{ one }}
-            </template>
-            <template #two="two">
-              {{ two }}
-            </template>
-          </foo>
-        `,
-        components: { Foo }
-      }).$mount()
-      expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(
-        `from foo default from foo one from foo two`
-      )
-    })
-
-    it('should warn mixed root-default and named slots', () => {
-      new Vue({
-        template: `
-          <foo #default="foo">
-            {{ foo }}
-            <template #one="one">
-              {{ one }}
-            </template>
-          </foo>
-        `,
-        components: { Foo }
-      }).$mount()
-      expect(`default slot should also use <template>`).toHaveBeenWarned()
-    })
-
-    it('shorthand without scope variable', () => {
-      const vm = new Vue({
-        template: `
-          <foo>
-            <template #one>one</template>
-            <template #two>two</template>
-          </foo>
-        `,
-        components: { Foo }
-      }).$mount()
-      expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`onetwo`)
-    })
-
-    it('shorthand named slots on root', () => {
-      const vm = new Vue({
-        template: `
-          <foo #one="one">
-            {{ one }}
-          </foo>
-        `,
-        components: { Foo }
-      }).$mount()
-      expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one`)
-    })
-
-    it('dynamic slot name', done => {
-      const vm = new Vue({
-        data: {
-          a: 'one',
-          b: 'two'
-        },
-        template: `
-          <foo>
-            <template #[a]="one">a {{ one }} </template>
-            <template v-slot:[b]="two">b {{ two }} </template>
-          </foo>
-        `,
-        components: { Foo }
-      }).$mount()
-      expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(
-        `a from foo one b from foo two`
-      )
-      vm.a = 'two'
-      vm.b = 'one'
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(
-          `b from foo one a from foo two `
-        )
-      }).then(done)
-    })
-
-    it('should work with v-if/v-else', done => {
-      const vm = new Vue({
-        data: { flag: true },
-        template: `
-          <foo>
-            <template v-if="flag" v-slot:one="one">a {{ one }} </template>
-            <template v-else v-slot:two="two">b {{ two }} </template>
-          </foo>
-        `,
-        components: { Foo }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe(`a from foo one `)
-      vm.flag = false
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe(`b from foo two `)
-      }).then(done)
-    })
-
-    it('warn when v-slot used on non-root <template>', () => {
-      // @ts-ignore unused
-      const vm = new Vue({
-        template: `
-          <foo>
-            <template v-if="true">
-              <template v-slot:one>foo</template>
-            </template>
-          </foo>
-        `,
-        components: { Foo }
-      }).$mount()
-      expect(
-        `<template v-slot> can only appear at the root level`
-      ).toHaveBeenWarned()
-    })
-  })
-
-  // 2.6 scoped slot perf optimization
-  it('should have accurate tracking for scoped slots', done => {
-    const parentUpdate = vi.fn()
-    const childUpdate = vi.fn()
-    const vm = new Vue({
-      template: `
-        <div>{{ parentCount }}<foo #default>{{ childCount }}</foo></div>
-      `,
-      data: {
-        parentCount: 0,
-        childCount: 0
-      },
-      updated: parentUpdate,
-      components: {
-        foo: {
-          template: `<div><slot/></div>`,
-          updated: childUpdate
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toMatch(`0<div>0</div>`)
-
-    vm.parentCount++
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toMatch(`1<div>0</div>`)
-      // should only trigger parent update
-      expect(parentUpdate.mock.calls.length).toBe(1)
-      expect(childUpdate.mock.calls.length).toBe(0)
-
-      vm.childCount++
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toMatch(`1<div>1</div>`)
-        // should only trigger child update
-        expect(parentUpdate.mock.calls.length).toBe(1)
-        expect(childUpdate.mock.calls.length).toBe(1)
-      })
-      .then(done)
-  })
-
-  // #9432: async components inside a scoped slot should trigger update of the
-  // component that invoked the scoped slot, not the lexical context component.
-  it('async component inside scoped slot', done => {
-    const vm = new Vue({
-      template: `
-        <foo>
-          <template #default>
-            <bar />
-          </template>
-        </foo>
-      `,
-      components: {
-        foo: {
-          template: `<div>foo<slot/></div>`
-        },
-        bar: resolve => {
-          setTimeout(() => {
-            resolve({
-              template: `<div>bar</div>`
-            })
-            next()
-          }, 0)
-        }
-      }
-    }).$mount()
-
-    function next() {
-      waitForUpdate(() => {
-        expect(vm.$el.textContent).toBe(`foobar`)
-      }).then(done)
-    }
-  })
-
-  // regression #9396
-  it('should not force update child with no slot content', done => {
-    const Child = {
-      updated: vi.fn(),
-      template: `<div></div>`
-    }
-
-    const parent = new Vue({
-      template: `<div>{{ count }}<child/></div>`,
-      data: {
-        count: 0
-      },
-      components: { Child }
-    }).$mount()
-
-    expect(parent.$el.textContent).toBe(`0`)
-    parent.count++
-    waitForUpdate(() => {
-      expect(parent.$el.textContent).toBe(`1`)
-      expect(Child.updated).not.toHaveBeenCalled()
-    }).then(done)
-  })
-
-  // regression #9438
-  it('nested scoped slots update', done => {
-    const Wrapper = {
-      template: `<div><slot/></div>`
-    }
-
-    const Inner = {
-      props: ['foo'],
-      template: `<div>{{ foo }}</div>`
-    }
-
-    const Outer = {
-      data: () => ({ foo: 1 }),
-      template: `<div><slot :foo="foo" /></div>`
-    }
-
-    const vm = new Vue({
-      components: { Outer, Wrapper, Inner },
-      template: `
-        <outer ref="outer" v-slot="props">
-          <wrapper v-slot>
-            <inner :foo="props.foo"/>
-          </wrapper>
-        </outer>
-      `
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe(`1`)
-
-    vm.$refs.outer.foo++
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe(`2`)
-    }).then(done)
-  })
-
-  it('dynamic v-bind arguments on <slot>', done => {
-    const Foo = {
-      data() {
-        return {
-          key: 'msg'
-        }
-      },
-      template: `<div><slot :[key]="'hello'"/></div>`
-    }
-
-    const vm = new Vue({
-      components: { Foo },
-      template: `
-        <foo ref="foo" v-slot="props">{{ props }}</foo>
-      `
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe(JSON.stringify({ msg: 'hello' }, null, 2))
-
-    vm.$refs.foo.key = 'changed'
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe(
-        JSON.stringify({ changed: 'hello' }, null, 2)
-      )
-    }).then(done)
-  })
-
-  // #9452
-  it('fallback for scoped slots passed multiple levels down', () => {
-    const inner = {
-      template: `<div><slot>fallback</slot></div>`
-    }
-
-    const wrapper = {
-      template: `
-        <inner>
-          <template #default>
-            <slot/>
-          </template>
-        </inner>
-      `,
-      components: { inner }
-    }
-
-    const vm = new Vue({
-      components: { wrapper, inner },
-      template: `<wrapper/>`
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe(`fallback`)
-  })
-
-  it('should expose v-slot without scope on this.$slots', () => {
-    const vm = new Vue({
-      template: `<foo><template v-slot>hello</template></foo>`,
-      components: {
-        foo: {
-          render(h) {
-            return h('div', this.$slots.default)
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('hello')
-  })
-
-  it('should not expose legacy syntax scoped slot on this.$slots', () => {
-    const vm = new Vue({
-      template: `<foo><template slot-scope="foo">hello</template></foo>`,
-      components: {
-        foo: {
-          render(h) {
-            expect(this.$slots.default).toBeUndefined()
-            return h('div', this.$slots.default)
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('')
-  })
-
-  it('should expose v-slot without scope on ctx.slots() in functional', () => {
-    const vm = new Vue({
-      template: `<foo><template v-slot>hello</template></foo>`,
-      components: {
-        foo: {
-          functional: true,
-          render(h, ctx) {
-            return h('div', ctx.slots().default)
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('hello')
-  })
-
-  it('should not cache scoped slot normalization when there are a mix of normal and scoped slots', done => {
-    const foo = {
-      template: `<div><slot name="foo" /> <slot name="bar" /></div>`
-    }
-
-    const vm = new Vue({
-      data: {
-        msg: 'foo'
-      },
-      template: `
-        <foo>
-          <div slot="foo">{{ msg }}</div>
-          <template #bar><div>bar</div></template>
-        </foo>
-      `,
-      components: { foo }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe(`foo bar`)
-    vm.msg = 'baz'
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe(`baz bar`)
-    }).then(done)
-  })
-
-  // #9468
-  it('should support passing multiple args to scoped slot function', () => {
-    const foo = {
-      render() {
-        return this.$scopedSlots.default('foo', 'bar')
-      }
-    }
-
-    const vm = new Vue({
-      template: `<foo v-slot="foo, bar">{{ foo }} {{ bar }}</foo>`,
-      components: { foo }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('foo bar')
-  })
-
-  it('should not skip updates when a scoped slot contains parent <slot/> content', done => {
-    const inner = {
-      template: `<div><slot/></div>`
-    }
-
-    const wrapper = {
-      template: `<inner v-slot><slot/></inner>`,
-      components: { inner }
-    }
-
-    const vm = new Vue({
-      data() {
-        return {
-          ok: true
-        }
-      },
-      components: { wrapper },
-      template: `<wrapper><div>{{ ok ? 'foo' : 'bar' }}</div></wrapper>`
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('foo')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('bar')
-    }).then(done)
-  })
-
-  it('should not skip updates for v-slot inside v-for', done => {
-    const test = {
-      template: `<div><slot></slot></div>`
-    }
-
-    const vm = new Vue({
-      template: `
-      <div>
-        <div v-for="i in numbers">
-          <test v-slot>{{ i }}</test>
-        </div>
-      </div>
-      `,
-      components: { test },
-      data: {
-        numbers: [1]
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe(`1`)
-    vm.numbers = [2]
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe(`2`)
-    }).then(done)
-  })
-
-  // #9534
-  it('should detect conditional reuse with different slot content', done => {
-    const Foo = {
-      template: `<div><slot :n="1" /></div>`
-    }
-
-    const vm = new Vue({
-      components: { Foo },
-      data: {
-        ok: true
-      },
-      template: `
-        <div>
-          <div v-if="ok">
-            <foo v-slot="{ n }">{{ n }}</foo>
-          </div>
-          <div v-if="!ok">
-            <foo v-slot="{ n }">{{ n + 1 }}</foo>
-          </div>
-        </div>
-      `
-    }).$mount()
-
-    expect(vm.$el.textContent.trim()).toBe(`1`)
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent.trim()).toBe(`2`)
-    }).then(done)
-  })
-
-  // #9644
-  it('should factor presence of normal slots into scoped slots caching', done => {
-    const Wrapper = {
-      template: `<div>
-        <p>Default:<slot/></p>
-        <p>Content:<slot name='content'/></p>
-      </div>`
-    }
-
-    const vm = new Vue({
-      data: { ok: false },
-      components: { Wrapper },
-      template: `<wrapper>
-        <p v-if='ok'>ok</p>
-        <template #content>
-          <p v-if='ok'>ok</p>
-        </template>
-      </wrapper>`
-    }).$mount()
-
-    expect(vm.$el.textContent).not.toMatch(`Default:ok`)
-    expect(vm.$el.textContent).not.toMatch(`Content:ok`)
-    vm.ok = true
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toMatch(`Default:ok`)
-      expect(vm.$el.textContent).toMatch(`Content:ok`)
-      vm.ok = false
-    })
-      .then(() => {
-        expect(vm.$el.textContent).not.toMatch(`Default:ok`)
-        expect(vm.$el.textContent).not.toMatch(`Content:ok`)
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toMatch(`Default:ok`)
-        expect(vm.$el.textContent).toMatch(`Content:ok`)
-      })
-      .then(done)
-  })
-
-  //#9658
-  it('fallback for scoped slot with single v-if', () => {
-    const vm = new Vue({
-      template: `<test v-slot><template v-if="false">hi</template></test>`,
-      components: {
-        Test: {
-          template: `<div><slot>fallback</slot></div>`
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toMatch('fallback')
-  })
-
-  // #9699
-  // Component only has normal slots, but is passing down $scopedSlots directly
-  // $scopedSlots should not be marked as stable in this case
-  it('render function passing $scopedSlots w/ normal slots down', done => {
-    const one = {
-      template: `<div><slot name="footer"/></div>`
-    }
-
-    const two = {
-      render(h) {
-        return h(one, {
-          scopedSlots: this.$scopedSlots
-        })
-      }
-    }
-
-    const vm = new Vue({
-      data: { count: 0 },
-      render(h) {
-        return h(two, [h('span', { slot: 'footer' }, this.count)])
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toMatch(`0`)
-    vm.count++
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toMatch(`1`)
-    }).then(done)
-  })
-
-  // #11652
-  it('should update when switching between two components with slot and without slot', done => {
-    const Child = {
-      template: `<div><slot/></div>`
-    }
-
-    const parent = new Vue({
-      template: `<div>
-        <child v-if="flag"><template #default>foo</template></child>
-        <child v-else></child>
-      </div>`,
-      data: {
-        flag: true
-      },
-      components: { Child }
-    }).$mount()
-
-    expect(parent.$el.textContent).toMatch(`foo`)
-    parent.flag = false
-    waitForUpdate(() => {
-      expect(parent.$el.textContent).toMatch(``)
-    }).then(done)
-  })
-})
diff --git a/test/unit/features/component/component-slot.spec.ts b/test/unit/features/component/component-slot.spec.ts
deleted file mode 100644
index da57d328aff..00000000000
--- a/test/unit/features/component/component-slot.spec.ts
+++ /dev/null
@@ -1,1076 +0,0 @@
-import Vue from 'vue'
-
-describe('Component slot', () => {
-  let vm, child
-  function mount(options) {
-    vm = new Vue({
-      data: {
-        msg: 'parent message'
-      },
-      template: `<div><test>${options.parentContent || ''}</test></div>`,
-      components: {
-        test: {
-          template: options.childTemplate,
-          data() {
-            return {
-              msg: 'child message'
-            }
-          }
-        }
-      }
-    }).$mount()
-    child = vm.$children[0]
-  }
-
-  it('no content', () => {
-    mount({
-      childTemplate: '<div><slot></slot></div>'
-    })
-    expect(child.$el.childNodes.length).toBe(0)
-  })
-
-  it('default slot', done => {
-    mount({
-      childTemplate: '<div><slot></slot></div>',
-      parentContent: '<p>{{ msg }}</p>'
-    })
-    expect(child.$el.tagName).toBe('DIV')
-    expect(child.$el.children[0].tagName).toBe('P')
-    expect(child.$el.children[0].textContent).toBe('parent message')
-    vm.msg = 'changed'
-    waitForUpdate(() => {
-      expect(child.$el.children[0].textContent).toBe('changed')
-    }).then(done)
-  })
-
-  it('named slot', done => {
-    mount({
-      childTemplate: '<div><slot name="test"></slot></div>',
-      parentContent: '<p slot="test">{{ msg }}</p>'
-    })
-    expect(child.$el.tagName).toBe('DIV')
-    expect(child.$el.children[0].tagName).toBe('P')
-    expect(child.$el.children[0].textContent).toBe('parent message')
-    vm.msg = 'changed'
-    waitForUpdate(() => {
-      expect(child.$el.children[0].textContent).toBe('changed')
-    }).then(done)
-  })
-
-  it('named slot with 0 as a number', done => {
-    mount({
-      childTemplate: '<div><slot :name="0"></slot></div>',
-      parentContent: '<p :slot="0">{{ msg }}</p>'
-    })
-    expect(child.$el.tagName).toBe('DIV')
-    expect(child.$el.children[0].tagName).toBe('P')
-    expect(child.$el.children[0].textContent).toBe('parent message')
-    vm.msg = 'changed'
-    waitForUpdate(() => {
-      expect(child.$el.children[0].textContent).toBe('changed')
-    }).then(done)
-  })
-
-  it('fallback content', () => {
-    mount({
-      childTemplate: '<div><slot><p>{{msg}}</p></slot></div>'
-    })
-    expect(child.$el.children[0].tagName).toBe('P')
-    expect(child.$el.textContent).toBe('child message')
-  })
-
-  it('fallback content with multiple named slots', () => {
-    mount({
-      childTemplate: `
-        <div>
-          <slot name="a"><p>fallback a</p></slot>
-          <slot name="b">fallback b</slot>
-        </div>
-      `,
-      parentContent: '<p slot="b">slot b</p>'
-    })
-    expect(child.$el.children.length).toBe(2)
-    expect(child.$el.children[0].textContent).toBe('fallback a')
-    expect(child.$el.children[1].textContent).toBe('slot b')
-  })
-
-  it('fallback content with mixed named/unnamed slots', () => {
-    mount({
-      childTemplate: `
-        <div>
-          <slot><p>fallback a</p></slot>
-          <slot name="b">fallback b</slot>
-        </div>
-      `,
-      parentContent: '<p slot="b">slot b</p>'
-    })
-    expect(child.$el.children.length).toBe(2)
-    expect(child.$el.children[0].textContent).toBe('fallback a')
-    expect(child.$el.children[1].textContent).toBe('slot b')
-  })
-
-  it('it should work with previous versions of the templates', () => {
-    const Test = {
-      render() {
-        const _vm = this
-        // const _h = _vm.$createElement;
-        const _c = _vm._self._c || vm._h
-        return _c(
-          'div',
-          [_vm._t('default', [_c('p', [_vm._v('slot default')])])],
-          2
-        )
-      }
-    }
-    let vm = new Vue({
-      template: `<test/>`,
-      components: { Test }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('slot default')
-    vm = new Vue({
-      template: `<test>custom content</test>`,
-      components: { Test }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('custom content')
-  })
-
-  it('fallback content should not be evaluated when the parent is providing it', () => {
-    const test = vi.fn()
-    const vm = new Vue({
-      template: '<test>slot default</test>',
-      components: {
-        test: {
-          template: '<div><slot>{{test()}}</slot></div>',
-          methods: {
-            test() {
-              test()
-              return 'test'
-            }
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('slot default')
-    expect(test).not.toHaveBeenCalled()
-  })
-
-  it('selector matching multiple elements', () => {
-    mount({
-      childTemplate: '<div><slot name="t"></slot></div>',
-      parentContent: '<p slot="t">1</p><div></div><p slot="t">2</p>'
-    })
-    expect(child.$el.innerHTML).toBe('<p>1</p><p>2</p>')
-  })
-
-  it('default content should only render parts not selected', () => {
-    mount({
-      childTemplate: `
-        <div>
-          <slot name="a"></slot>
-          <slot></slot>
-          <slot name="b"></slot>
-        </div>
-      `,
-      parentContent: '<div>foo</div><p slot="a">1</p><p slot="b">2</p>'
-    })
-    expect(child.$el.innerHTML).toBe('<p>1</p> <div>foo</div> <p>2</p>')
-  })
-
-  it('name should only match children', function () {
-    mount({
-      childTemplate: `
-        <div>
-          <slot name="a"><p>fallback a</p></slot>
-          <slot name="b"><p>fallback b</p></slot>
-          <slot name="c"><p>fallback c</p></slot>
-        </div>
-      `,
-      parentContent: `
-        '<p slot="b">select b</p>
-        '<span><p slot="b">nested b</p></span>
-        '<span><p slot="c">nested c</p></span>
-      `
-    })
-    expect(child.$el.children.length).toBe(3)
-    expect(child.$el.children[0].textContent).toBe('fallback a')
-    expect(child.$el.children[1].textContent).toBe('select b')
-    expect(child.$el.children[2].textContent).toBe('fallback c')
-  })
-
-  it('should accept expressions in slot attribute and slot names', () => {
-    mount({
-      childTemplate: `<div><slot :name="'a'"></slot></div>`,
-      parentContent: `<p>one</p><p :slot="'a'">two</p>`
-    })
-    expect(child.$el.innerHTML).toBe('<p>two</p>')
-  })
-
-  it('slot inside v-if', done => {
-    const vm = new Vue({
-      data: {
-        a: 1,
-        b: 2,
-        show: true
-      },
-      template: '<test :show="show"><p slot="b">{{b}}</p><p>{{a}}</p></test>',
-      components: {
-        test: {
-          props: ['show'],
-          template: '<div v-if="show"><slot></slot><slot name="b"></slot></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('12')
-    vm.a = 2
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('22')
-      vm.show = false
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('')
-        vm.show = true
-        vm.a = 3
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('32')
-      })
-      .then(done)
-  })
-
-  it('slot inside v-for', () => {
-    mount({
-      childTemplate: '<div><slot v-for="i in 3" :name="i"></slot></div>',
-      parentContent: '<p v-for="i in 3" :slot="i">{{ i - 1 }}</p>'
-    })
-    expect(child.$el.innerHTML).toBe('<p>0</p><p>1</p><p>2</p>')
-  })
-
-  it('nested slots', done => {
-    const vm = new Vue({
-      template: '<test><test2><p>{{ msg }}</p></test2></test>',
-      data: {
-        msg: 'foo'
-      },
-      components: {
-        test: {
-          template: '<div><slot></slot></div>'
-        },
-        test2: {
-          template: '<div><slot></slot></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<div><p>foo</p></div>')
-    vm.msg = 'bar'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<div><p>bar</p></div>')
-    }).then(done)
-  })
-
-  it('v-if on inserted content', done => {
-    const vm = new Vue({
-      template: '<test><p v-if="ok">{{ msg }}</p></test>',
-      data: {
-        ok: true,
-        msg: 'hi'
-      },
-      components: {
-        test: {
-          template: '<div><slot>fallback</slot></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<p>hi</p>')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('fallback')
-      vm.ok = true
-      vm.msg = 'bye'
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<p>bye</p>')
-      })
-      .then(done)
-  })
-
-  it('template slot', function () {
-    const vm = new Vue({
-      template: '<test><template slot="test">hello</template></test>',
-      components: {
-        test: {
-          template: '<div><slot name="test"></slot> world</div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('hello world')
-  })
-
-  it('combined with v-for', () => {
-    const vm = new Vue({
-      template: '<div><test v-for="i in 3" :key="i">{{ i }}</test></div>',
-      components: {
-        test: {
-          template: '<div><slot></slot></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
-  })
-
-  it('inside template v-if', () => {
-    mount({
-      childTemplate: `
-        <div>
-          <template v-if="true"><slot></slot></template>
-        </div>
-      `,
-      parentContent: 'foo'
-    })
-    expect(child.$el.innerHTML).toBe('foo')
-  })
-
-  it('default slot should use fallback content if has only whitespace', () => {
-    mount({
-      childTemplate: `
-        <div>
-          <slot name="first"><p>first slot</p></slot>
-          <slot><p>this is the default slot</p></slot>
-          <slot name="second"><p>second named slot</p></slot>
-        </div>
-      `,
-      parentContent: `<div slot="first">1</div> <div slot="second">2</div> <div slot="second">2+</div>`
-    })
-    expect(child.$el.innerHTML).toBe(
-      '<div>1</div> <p>this is the default slot</p> <div>2</div><div>2+</div>'
-    )
-  })
-
-  it('programmatic access to $slots', () => {
-    const vm = new Vue({
-      template: '<test><p slot="a">A</p><div>C</div><p slot="b">B</p></test>',
-      components: {
-        test: {
-          render() {
-            expect(this.$slots.a.length).toBe(1)
-            expect(this.$slots.a[0].tag).toBe('p')
-            expect(this.$slots.a[0].children.length).toBe(1)
-            expect(this.$slots.a[0].children[0].text).toBe('A')
-
-            expect(this.$slots.b.length).toBe(1)
-            expect(this.$slots.b[0].tag).toBe('p')
-            expect(this.$slots.b[0].children.length).toBe(1)
-            expect(this.$slots.b[0].children[0].text).toBe('B')
-
-            expect(this.$slots.default.length).toBe(1)
-            expect(this.$slots.default[0].tag).toBe('div')
-            expect(this.$slots.default[0].children.length).toBe(1)
-            expect(this.$slots.default[0].children[0].text).toBe('C')
-
-            return this.$slots.default[0]
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('DIV')
-    expect(vm.$el.textContent).toBe('C')
-  })
-
-  it('warn if user directly returns array', () => {
-    new Vue({
-      template: '<test><div slot="foo"></div><div slot="foo"></div></test>',
-      components: {
-        test: {
-          render() {
-            return this.$slots.foo
-          }
-        }
-      }
-    }).$mount()
-    expect(
-      'Render function should return a single root node'
-    ).toHaveBeenWarned()
-  })
-
-  // #3254
-  it('should not keep slot name when passed further down', () => {
-    const vm = new Vue({
-      template: '<test><span slot="foo">foo</span></test>',
-      components: {
-        test: {
-          template: '<child><slot name="foo"></slot></child>',
-          components: {
-            child: {
-              template: `
-                <div>
-                  <div class="default"><slot></slot></div>
-                  <div class="named"><slot name="foo"></slot></div>
-                </div>
-              `
-            }
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.querySelector('.default').textContent).toBe('foo')
-    expect(vm.$el.querySelector('.named').textContent).toBe('')
-  })
-
-  it('should not keep slot name when passed further down (nested)', () => {
-    const vm = new Vue({
-      template: '<wrap><test><span slot="foo">foo</span></test></wrap>',
-      components: {
-        wrap: {
-          template: '<div><slot></slot></div>'
-        },
-        test: {
-          template: '<child><slot name="foo"></slot></child>',
-          components: {
-            child: {
-              template: `
-                <div>
-                  <div class="default"><slot></slot></div>
-                  <div class="named"><slot name="foo"></slot></div>
-                </div>
-              `
-            }
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.querySelector('.default').textContent).toBe('foo')
-    expect(vm.$el.querySelector('.named').textContent).toBe('')
-  })
-
-  it('should not keep slot name when passed further down (functional)', () => {
-    const child = {
-      template: `
-        <div>
-          <div class="default"><slot></slot></div>
-          <div class="named"><slot name="foo"></slot></div>
-        </div>
-      `
-    }
-
-    const vm = new Vue({
-      template: '<test><span slot="foo">foo</span></test>',
-      components: {
-        test: {
-          functional: true,
-          render(h, ctx) {
-            const slots = ctx.slots()
-            return h(child, slots.foo)
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.querySelector('.default').textContent).toBe('foo')
-    expect(vm.$el.querySelector('.named').textContent).toBe('')
-  })
-
-  // #3400
-  it('named slots should be consistent across re-renders', done => {
-    const vm = new Vue({
-      template: `
-        <comp>
-          <div slot="foo">foo</div>
-        </comp>
-      `,
-      components: {
-        comp: {
-          data() {
-            return { a: 1 }
-          },
-          template: `<div><slot name="foo"></slot>{{ a }}</div>`
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('foo1')
-    vm.$children[0].a = 2
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('foo2')
-    }).then(done)
-  })
-
-  // #3437
-  it('should correctly re-create components in slot', done => {
-    const calls: any[] = []
-    const vm = new Vue({
-      template: `
-        <comp ref="child">
-          <div slot="foo">
-            <child></child>
-          </div>
-        </comp>
-      `,
-      components: {
-        comp: {
-          data() {
-            return { ok: true }
-          },
-          template: `<div><slot name="foo" v-if="ok"></slot></div>`
-        },
-        child: {
-          template: '<div>child</div>',
-          created() {
-            calls.push(1)
-          },
-          destroyed() {
-            calls.push(2)
-          }
-        }
-      }
-    }).$mount()
-
-    expect(calls).toEqual([1])
-    vm.$refs.child.ok = false
-    waitForUpdate(() => {
-      expect(calls).toEqual([1, 2])
-      vm.$refs.child.ok = true
-    })
-      .then(() => {
-        expect(calls).toEqual([1, 2, 1])
-        vm.$refs.child.ok = false
-      })
-      .then(() => {
-        expect(calls).toEqual([1, 2, 1, 2])
-      })
-      .then(done)
-  })
-
-  it('should support duplicate slots', done => {
-    const vm = new Vue({
-      template: `
-        <foo ref="foo">
-          <div slot="a">{{ n }}</div>
-        </foo>
-      `,
-      data: {
-        n: 1
-      },
-      components: {
-        foo: {
-          data() {
-            return { ok: true }
-          },
-          template: `
-            <div>
-              <slot name="a" />
-              <slot v-if="ok" name="a" />
-              <pre><slot name="a" /></pre>
-            </div>
-          `
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      `<div>1</div> <div>1</div> <pre><div>1</div></pre>`
-    )
-    vm.n++
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        `<div>2</div> <div>2</div> <pre><div>2</div></pre>`
-      )
-      vm.n++
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<div>3</div> <div>3</div> <pre><div>3</div></pre>`
-        )
-        vm.$refs.foo.ok = false
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<div>3</div> <!----> <pre><div>3</div></pre>`
-        )
-        vm.n++
-        vm.$refs.foo.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          `<div>4</div> <div>4</div> <pre><div>4</div></pre>`
-        )
-      })
-      .then(done)
-  })
-
-  // #3518
-  it('events should not break when slot is toggled by v-if', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      template: `<test><div class="click" @click="test">hi</div></test>`,
-      methods: {
-        test: spy
-      },
-      components: {
-        test: {
-          data: () => ({
-            toggle: true
-          }),
-          template: `<div v-if="toggle"><slot></slot></div>`
-        }
-      }
-    }).$mount()
-
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.textContent).toBe('hi')
-    vm.$children[0].toggle = false
-    waitForUpdate(() => {
-      vm.$children[0].toggle = true
-    })
-      .then(() => {
-        global.triggerEvent(vm.$el.querySelector('.click'), 'click')
-        expect(spy).toHaveBeenCalled()
-      })
-      .then(() => {
-        document.body.removeChild(vm.$el)
-      })
-      .then(done)
-  })
-
-  it('renders static tree with text', () => {
-    const vm = new Vue({
-      template: `<div><test><template><div></div>Hello<div></div></template></test></div>`,
-      components: {
-        test: {
-          template: '<div><slot></slot></div>'
-        }
-      }
-    })
-    vm.$mount()
-    expect('Error when rendering root').not.toHaveBeenWarned()
-  })
-
-  // #3872
-  it('functional component as slot', () => {
-    const vm = new Vue({
-      template: `
-        <parent>
-          <child>one</child>
-          <child slot="a">two</child>
-        </parent>
-      `,
-      components: {
-        parent: {
-          template: `<div><slot name="a"></slot><slot></slot></div>`
-        },
-        child: {
-          functional: true,
-          render(h, { slots }) {
-            return h('div', slots().default)
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML.trim()).toBe('<div>two</div><div>one</div>')
-  })
-
-  // #4209
-  it('slot of multiple text nodes should not be infinitely merged', done => {
-    const wrap = {
-      template: `<inner ref="inner">foo<slot></slot></inner>`,
-      components: {
-        inner: {
-          data: () => ({ a: 1 }),
-          template: `<div>{{a}}<slot></slot></div>`
-        }
-      }
-    }
-    const vm = new Vue({
-      template: `<wrap ref="wrap">bar</wrap>`,
-      components: { wrap }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('1foobar')
-    vm.$refs.wrap.$refs.inner.a++
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('2foobar')
-    }).then(done)
-  })
-
-  // #4315
-  it('functional component passing slot content to stateful child component', done => {
-    const ComponentWithSlots = {
-      render(h) {
-        return h('div', this.$slots.slot1)
-      }
-    }
-
-    const FunctionalComp = {
-      functional: true,
-      render(h) {
-        return h(ComponentWithSlots, [h('span', { slot: 'slot1' }, 'foo')])
-      }
-    }
-
-    const vm = new Vue({
-      data: { n: 1 },
-      render(h) {
-        return h('div', [this.n, h(FunctionalComp)])
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('1foo')
-    vm.n++
-    waitForUpdate(() => {
-      // should not lose named slot
-      expect(vm.$el.textContent).toBe('2foo')
-    }).then(done)
-  })
-
-  it('the elements of slot should be updated correctly', done => {
-    const vm = new Vue({
-      data: { n: 1 },
-      template:
-        '<div><test><span v-for="i in n" :key="i">{{ i }}</span><input value="a"/></test></div>',
-      components: {
-        test: {
-          template: '<div><slot></slot></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<div><span>1</span><input value="a"></div>')
-    const input = vm.$el.querySelector('input')
-    input.value = 'b'
-    vm.n++
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<div><span>1</span><span>2</span><input value="a"></div>'
-      )
-      expect(vm.$el.querySelector('input')).toBe(input)
-      expect(vm.$el.querySelector('input').value).toBe('b')
-    }).then(done)
-  })
-
-  // GitHub issue #5888
-  it('should resolve correctly slot with keep-alive', () => {
-    const vm = new Vue({
-      template: `
-      <div>
-        <container>
-          <keep-alive slot="foo">
-            <child></child>
-          </keep-alive>
-        </container>
-      </div>
-      `,
-      components: {
-        container: {
-          template:
-            '<div><slot>default</slot><slot name="foo">named</slot></div>'
-        },
-        child: {
-          template: '<span>foo</span>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<div>default<span>foo</span></div>')
-  })
-
-  // #6372, #6915
-  it('should handle nested components in slots properly', done => {
-    const TestComponent = {
-      template: `
-        <component :is="toggleEl ? 'b' : 'i'">
-          <slot />
-        </component>
-      `,
-      data() {
-        return {
-          toggleEl: true
-        }
-      }
-    }
-
-    const vm = new Vue({
-      template: `
-        <div>
-          <test-component ref="test">
-            <div>
-              <foo/>
-            </div>
-            <bar>
-              <foo/>
-            </bar>
-          </test-component>
-        </div>
-      `,
-      components: {
-        TestComponent,
-        foo: {
-          template: `<div>foo</div>`
-        },
-        bar: {
-          template: `<div>bar<slot/></div>`
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe(
-      `<b><div><div>foo</div></div> <div>bar<div>foo</div></div></b>`
-    )
-
-    vm.$refs.test.toggleEl = false
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        `<i><div><div>foo</div></div> <div>bar<div>foo</div></div></i>`
-      )
-    }).then(done)
-  })
-
-  it('should preserve slot attribute if not absorbed by a Vue component', () => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <div slot="foo"></div>
-        </div>
-      `
-    }).$mount()
-    expect(vm.$el.children[0].getAttribute('slot')).toBe('foo')
-  })
-
-  it('passing a slot down as named slot', () => {
-    const Bar = {
-      template: `<div class="bar"><slot name="foo"/></div>`
-    }
-
-    const Foo = {
-      components: { Bar },
-      template: `<div class="foo"><bar><slot slot="foo"/></bar></div>`
-    }
-
-    const vm = new Vue({
-      components: { Foo },
-      template: `<div><foo>hello</foo></div>`
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe(
-      '<div class="foo"><div class="bar">hello</div></div>'
-    )
-  })
-
-  it('fallback content for named template slot', () => {
-    const Bar = {
-      template: `<div class="bar"><slot name="foo">fallback</slot></div>`
-    }
-
-    const Foo = {
-      components: { Bar },
-      template: `<div class="foo"><bar><template slot="foo"/><slot/></template></bar></div>`
-    }
-
-    const vm = new Vue({
-      components: { Foo },
-      template: `<div><foo></foo></div>`
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe(
-      '<div class="foo"><div class="bar">fallback</div></div>'
-    )
-  })
-
-  // #7106
-  it('should not lose functional slot across renders', done => {
-    const One = {
-      data: () => ({
-        foo: true
-      }),
-      render(h) {
-        this.foo
-        return h('div', this.$slots.slot)
-      }
-    }
-
-    const Two = {
-      render(h) {
-        return h('span', this.$slots.slot)
-      }
-    }
-
-    const Three = {
-      functional: true,
-      render: (h, { children }) => h('span', children)
-    }
-
-    const vm = new Vue({
-      template: `
-        <div>
-          <one ref="one">
-            <two slot="slot">
-              <three slot="slot">hello</three>
-            </two>
-          </one>
-        </div>
-      `,
-      components: { One, Two, Three }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('hello')
-    // trigger re-render of <one>
-    vm.$refs.one.foo = false
-    waitForUpdate(() => {
-      // should still be there
-      expect(vm.$el.textContent).toBe('hello')
-    }).then(done)
-  })
-
-  it('should allow passing named slots as raw children down multiple layers of functional component', () => {
-    const CompB = {
-      functional: true,
-      render(h, { slots }) {
-        return slots().foo
-      }
-    }
-
-    const CompA = {
-      functional: true,
-      render(h, { children }) {
-        return h(CompB, children)
-      }
-    }
-
-    const vm = new Vue({
-      components: {
-        CompA
-      },
-      template: `
-        <div>
-          <comp-a>
-            <span slot="foo">foo</span>
-          </comp-a>
-        </div>
-      `
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('foo')
-  })
-
-  // #7817
-  it('should not match wrong named slot in functional component on re-render', done => {
-    const Functional = {
-      functional: true,
-      render: (h, ctx) => ctx.slots().default
-    }
-
-    const Stateful = {
-      data() {
-        return { ok: true }
-      },
-      render(h) {
-        this.ok // register dep
-        return h('div', [h(Functional, this.$slots.named)])
-      }
-    }
-
-    const vm = new Vue({
-      template: `<stateful ref="stateful"><div slot="named">foo</div></stateful>`,
-      components: { Stateful }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('foo')
-    vm.$refs.stateful.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('foo')
-    }).then(done)
-  })
-
-  // #7975
-  it('should update named slot correctly when its position in the tree changed', done => {
-    const ChildComponent = {
-      template: '<b>{{ message }}</b>',
-      props: ['message']
-    }
-    let parentVm
-    const ParentComponent = {
-      template: `
-        <div>
-          <span v-if="alter">
-            <span><slot name="foo" /></span>
-          </span>
-          <span v-else>
-            <slot name="foo" />
-          </span>
-        </div>
-      `,
-      data() {
-        return {
-          alter: true
-        }
-      },
-      mounted() {
-        parentVm = this
-      }
-    }
-    const vm = new Vue({
-      template: `
-        <parent-component>
-          <span slot="foo">
-            <child-component :message="message" />
-          </span>
-        </parent-component>
-      `,
-      components: {
-        ChildComponent,
-        ParentComponent
-      },
-      data() {
-        return {
-          message: 1
-        }
-      }
-    }).$mount()
-    expect(vm.$el.firstChild.innerHTML).toBe(
-      '<span><span><b>1</b></span></span>'
-    )
-    parentVm.alter = false
-    waitForUpdate(() => {
-      vm.message = 2
-    })
-      .then(() => {
-        expect(vm.$el.firstChild.innerHTML).toBe('<span><b>2</b></span>')
-      })
-      .then(done)
-  })
-
-  // #12102
-  it('v-if inside scoped slot', () => {
-    const vm = new Vue({
-      template: `<test><template #custom><span v-if="false">a</span><span>b</span></template></test>`,
-      components: {
-        test: {
-          template: `<div><slot name="custom"/></div>`
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe(`<!----><span>b</span>`)
-  })
-
-  // regression 2.7.0-alpha.4
-  it('passing scoped slots through nested parent chain', () => {
-    const Foo = {
-      template: `
-        <div><slot>foo default</slot></div>
-      `
-    }
-
-    const Bar = {
-      components: { Foo },
-      template: `<Foo><slot name="bar"/></Foo>`
-    }
-
-    const App = {
-      components: { Bar },
-      template: `<Bar>
-        <template #bar>
-          <span>App content for Bar#bar</span>
-        </template>
-      </Bar>`
-    }
-
-    const vm = new Vue({
-      render: h => h(App)
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toMatch(`App content for Bar#bar`)
-  })
-})
diff --git a/test/unit/features/component/component.spec.ts b/test/unit/features/component/component.spec.ts
deleted file mode 100644
index 510ad2473ba..00000000000
--- a/test/unit/features/component/component.spec.ts
+++ /dev/null
@@ -1,459 +0,0 @@
-import Vue from 'vue'
-
-describe('Component', () => {
-  it('static', () => {
-    const vm = new Vue({
-      template: '<test></test>',
-      components: {
-        test: {
-          data() {
-            return { a: 123 }
-          },
-          template: '<span>{{a}}</span>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('SPAN')
-    expect(vm.$el.innerHTML).toBe('123')
-  })
-
-  it('using component in restricted elements', () => {
-    const vm = new Vue({
-      template: '<div><table><tbody><test></test></tbody></table></div>',
-      components: {
-        test: {
-          data() {
-            return { a: 123 }
-          },
-          template: '<tr><td>{{a}}</td></tr>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      '<table><tbody><tr><td>123</td></tr></tbody></table>'
-    )
-  })
-
-  it('"is" attribute', () => {
-    const vm = new Vue({
-      template: '<div><table><tbody><tr is="test"></tr></tbody></table></div>',
-      components: {
-        test: {
-          data() {
-            return { a: 123 }
-          },
-          template: '<tr><td>{{a}}</td></tr>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      '<table><tbody><tr><td>123</td></tr></tbody></table>'
-    )
-  })
-
-  it('inline-template', () => {
-    const vm = new Vue({
-      template: '<div><test inline-template><span>{{a}}</span></test></div>',
-      data: {
-        a: 'parent'
-      },
-      components: {
-        test: {
-          data() {
-            return { a: 'child' }
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>child</span>')
-  })
-
-  it('fragment instance warning', () => {
-    new Vue({
-      template: '<test></test>',
-      components: {
-        test: {
-          data() {
-            return { a: 123, b: 234 }
-          },
-          template: '<p>{{a}}</p><p>{{b}}</p>'
-        }
-      }
-    }).$mount()
-    expect(
-      'Component template should contain exactly one root element'
-    ).toHaveBeenWarned()
-  })
-
-  it('dynamic', done => {
-    const vm = new Vue({
-      template: '<component :is="view" :view="view"></component>',
-      data: {
-        view: 'view-a'
-      },
-      components: {
-        'view-a': {
-          template: '<div>foo {{view}}</div>',
-          data() {
-            return { view: 'a' }
-          }
-        },
-        'view-b': {
-          template: '<div>bar {{view}}</div>',
-          data() {
-            return { view: 'b' }
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.outerHTML).toBe('<div view="view-a">foo a</div>')
-    vm.view = 'view-b'
-    waitForUpdate(() => {
-      expect(vm.$el.outerHTML).toBe('<div view="view-b">bar b</div>')
-      vm.view = ''
-    })
-      .then(() => {
-        expect(vm.$el.nodeType).toBe(8)
-        expect(vm.$el.data).toBe('')
-      })
-      .then(done)
-  })
-
-  it('dynamic with props', done => {
-    const vm = new Vue({
-      template: '<component :is="view" :view="view"></component>',
-      data: {
-        view: 'view-a'
-      },
-      components: {
-        'view-a': {
-          template: '<div>foo {{view}}</div>',
-          props: ['view']
-        },
-        'view-b': {
-          template: '<div>bar {{view}}</div>',
-          props: ['view']
-        }
-      }
-    }).$mount()
-    expect(vm.$el.outerHTML).toBe('<div>foo view-a</div>')
-    vm.view = 'view-b'
-    waitForUpdate(() => {
-      expect(vm.$el.outerHTML).toBe('<div>bar view-b</div>')
-      vm.view = ''
-    })
-      .then(() => {
-        expect(vm.$el.nodeType).toBe(8)
-        expect(vm.$el.data).toBe('')
-      })
-      .then(done)
-  })
-
-  it(':is using raw component constructor', () => {
-    const vm = new Vue({
-      template:
-        '<div>' +
-        '<component :is="$options.components.test"></component>' +
-        '<component :is="$options.components.async"></component>' +
-        '</div>',
-      components: {
-        test: {
-          template: '<span>foo</span>'
-        },
-        async: function (resolve) {
-          resolve({
-            template: '<span>bar</span>'
-          })
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>foo</span><span>bar</span>')
-  })
-
-  it('dynamic combined with v-for', done => {
-    const vm = new Vue({
-      template:
-        '<div>' +
-        '<component v-for="(c, i) in comps" :key="i" :is="c.type"></component>' +
-        '</div>',
-      data: {
-        comps: [{ type: 'one' }, { type: 'two' }]
-      },
-      components: {
-        one: {
-          template: '<span>one</span>'
-        },
-        two: {
-          template: '<span>two</span>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>one</span><span>two</span>')
-    vm.comps[1].type = 'one'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<span>one</span><span>one</span>')
-    }).then(done)
-  })
-
-  it('dynamic elements with domProps', done => {
-    const vm = new Vue({
-      template: '<component :is="view" :value.prop="val"></component>',
-      data: {
-        view: 'input',
-        val: 'hello'
-      }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('INPUT')
-    expect(vm.$el.value).toBe('hello')
-    vm.view = 'textarea'
-    vm.val += ' world'
-    waitForUpdate(() => {
-      expect(vm.$el.tagName).toBe('TEXTAREA')
-      expect(vm.$el.value).toBe('hello world')
-      vm.view = ''
-    }).then(done)
-  })
-
-  it('should compile parent template directives & content in parent scope', done => {
-    const vm = new Vue({
-      data: {
-        ok: false,
-        message: 'hello'
-      },
-      template: '<test v-show="ok">{{message}}</test>',
-      components: {
-        test: {
-          template: '<div><slot></slot> {{message}}</div>',
-          data() {
-            return {
-              message: 'world'
-            }
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.style.display).toBe('none')
-    expect(vm.$el.textContent).toBe('hello world')
-    vm.ok = true
-    vm.message = 'bye'
-    waitForUpdate(() => {
-      expect(vm.$el.style.display).toBe('')
-      expect(vm.$el.textContent).toBe('bye world')
-    }).then(done)
-  })
-
-  it('parent content + v-if', done => {
-    const vm = new Vue({
-      data: {
-        ok: false,
-        message: 'hello'
-      },
-      template: '<test v-if="ok">{{message}}</test>',
-      components: {
-        test: {
-          template: '<div><slot></slot> {{message}}</div>',
-          data() {
-            return {
-              message: 'world'
-            }
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('')
-    expect(vm.$children.length).toBe(0)
-    vm.ok = true
-    waitForUpdate(() => {
-      expect(vm.$children.length).toBe(1)
-      expect(vm.$el.textContent).toBe('hello world')
-    }).then(done)
-  })
-
-  it('props', () => {
-    const vm = new Vue({
-      data: {
-        list: [{ a: 1 }, { a: 2 }]
-      },
-      template: '<test :collection="list"></test>',
-      components: {
-        test: {
-          template: '<ul><li v-for="item in collection">{{item.a}}</li></ul>',
-          props: ['collection']
-        }
-      }
-    }).$mount()
-    expect(vm.$el.outerHTML).toBe('<ul><li>1</li><li>2</li></ul>')
-  })
-
-  it('should warn when using camelCased props in in-DOM template', () => {
-    new Vue({
-      data: {
-        list: [{ a: 1 }, { a: 2 }]
-      },
-      template: '<test :somecollection="list"></test>', // <-- simulate lowercased template
-      components: {
-        test: {
-          template:
-            '<ul><li v-for="item in someCollection">{{item.a}}</li></ul>',
-          props: ['someCollection']
-        }
-      }
-    }).$mount()
-    expect(
-      'You should probably use "some-collection" instead of "someCollection".'
-    ).toHaveBeenTipped()
-  })
-
-  it('should warn when using camelCased events in in-DOM template', () => {
-    new Vue({
-      template: '<test @foobar="a++"></test>', // <-- simulate lowercased template
-      components: {
-        test: {
-          template: '<div></div>',
-          created() {
-            this.$emit('fooBar')
-          }
-        }
-      }
-    }).$mount()
-    expect(
-      'You should probably use "foo-bar" instead of "fooBar".'
-    ).toHaveBeenTipped()
-  })
-
-  it('not found component should not throw', () => {
-    expect(function () {
-      new Vue({
-        template: '<div is="non-existent"></div>'
-      })
-    }).not.toThrow()
-  })
-
-  it('properly update replaced higher-order component root node', done => {
-    const vm = new Vue({
-      data: {
-        color: 'red'
-      },
-      template: '<test id="foo" :class="color"></test>',
-      components: {
-        test: {
-          data() {
-            return { tag: 'div' }
-          },
-          render(h) {
-            return h(this.tag, { class: 'test' }, 'hi')
-          }
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.tagName).toBe('DIV')
-    expect(vm.$el.id).toBe('foo')
-    expect(vm.$el.className).toBe('test red')
-
-    vm.color = 'green'
-    waitForUpdate(() => {
-      expect(vm.$el.tagName).toBe('DIV')
-      expect(vm.$el.id).toBe('foo')
-      expect(vm.$el.className).toBe('test green')
-      vm.$children[0].tag = 'p'
-    })
-      .then(() => {
-        expect(vm.$el.tagName).toBe('P')
-        expect(vm.$el.id).toBe('foo')
-        expect(vm.$el.className).toBe('test green')
-        vm.color = 'red'
-      })
-      .then(() => {
-        expect(vm.$el.tagName).toBe('P')
-        expect(vm.$el.id).toBe('foo')
-        expect(vm.$el.className).toBe('test red')
-      })
-      .then(done)
-  })
-
-  it('catch component render error and preserve previous vnode', done => {
-    const spy = vi.fn()
-    Vue.config.errorHandler = spy
-    const vm = new Vue({
-      data: {
-        a: {
-          b: 123
-        }
-      },
-      render(h) {
-        return h('div', [this.a.b])
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('123')
-    expect(spy).not.toHaveBeenCalled()
-    vm.a = null
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalled()
-      expect(vm.$el.textContent).toBe('123') // should preserve rendered DOM
-      vm.a = { b: 234 }
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('234') // should be able to recover
-        Vue.config.errorHandler = undefined
-      })
-      .then(done)
-  })
-
-  it('relocates node without error', done => {
-    const el = document.createElement('div')
-    document.body.appendChild(el)
-    const target = document.createElement('div')
-    document.body.appendChild(target)
-
-    const Test = {
-      render(h) {
-        return h('div', { class: 'test' }, this.$slots.default)
-      },
-      mounted() {
-        target.appendChild(this.$el)
-      },
-      beforeDestroy() {
-        const parent = this.$el.parentNode
-        if (parent) {
-          parent.removeChild(this.$el)
-        }
-      }
-    }
-    const vm = new Vue({
-      data() {
-        return {
-          view: true
-        }
-      },
-      template: `<div><test v-if="view">Test</test></div>`,
-      components: {
-        test: Test
-      }
-    }).$mount(el)
-
-    expect(el.outerHTML).toBe('<div></div>')
-    expect(target.outerHTML).toBe('<div><div class="test">Test</div></div>')
-    vm.view = false
-    waitForUpdate(() => {
-      expect(el.outerHTML).toBe('<div></div>')
-      expect(target.outerHTML).toBe('<div></div>')
-      vm.$destroy()
-    }).then(done)
-  })
-
-  it('render vnode with <script> tag as root element', () => {
-    const vm = new Vue({
-      template: '<scriptTest></scriptTest>',
-      components: {
-        scriptTest: {
-          template: '<script>console.log(1)</script>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.nodeName).toBe('#comment')
-    expect(
-      'Templates should only be responsible for mapping the state'
-    ).toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/debug.spec.ts b/test/unit/features/debug.spec.ts
deleted file mode 100644
index 49398b4c1f7..00000000000
--- a/test/unit/features/debug.spec.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import Vue from 'vue'
-import { formatComponentName, warn } from 'core/util/debug'
-
-describe('Debug utilities', () => {
-  it('properly format component names', () => {
-    const vm = new Vue()
-    expect(formatComponentName(vm)).toBe('<Root>')
-
-    vm.$root = null
-    vm.$options.name = 'hello-there'
-    expect(formatComponentName(vm)).toBe('<HelloThere>')
-
-    vm.$options.name = null
-    vm.$options._componentTag = 'foo-bar-1'
-    expect(formatComponentName(vm)).toBe('<FooBar1>')
-
-    vm.$options._componentTag = null
-    vm.$options.__file = '/foo/bar/baz/SomeThing.vue'
-    expect(formatComponentName(vm)).toBe(`<SomeThing> at ${vm.$options.__file}`)
-    expect(formatComponentName(vm, false)).toBe('<SomeThing>')
-
-    vm.$options.__file = 'C:\\foo\\bar\\baz\\windows_file.vue'
-    expect(formatComponentName(vm)).toBe(
-      `<WindowsFile> at ${vm.$options.__file}`
-    )
-    expect(formatComponentName(vm, false)).toBe('<WindowsFile>')
-  })
-
-  it('generate correct component hierarchy trace', () => {
-    const one = {
-      name: 'one',
-      render: h => h(two)
-    }
-    const two = {
-      name: 'two',
-      render: h => h(three)
-    }
-    const three = {
-      name: 'three'
-    }
-    new Vue({
-      render: h => h(one)
-    }).$mount()
-
-    expect(
-      `Failed to mount component: template or render function not defined.
-
-found in
-
----> <Three>
-       <Two>
-         <One>
-           <Root>`
-    ).toHaveBeenWarned()
-  })
-
-  it('generate correct component hierarchy trace (recursive)', () => {
-    let i = 0
-    const one = {
-      name: 'one',
-      render: h => (i++ < 5 ? h(one) : h(two))
-    }
-    const two = {
-      name: 'two',
-      render: h => h(three)
-    }
-    const three = {
-      name: 'three'
-    }
-    new Vue({
-      render: h => h(one)
-    }).$mount()
-
-    expect(
-      `Failed to mount component: template or render function not defined.
-
-found in
-
----> <Three>
-       <Two>
-         <One>... (5 recursive calls)
-           <Root>`
-    ).toHaveBeenWarned()
-  })
-
-  describe('warn', () => {
-    const msg = 'message'
-    const vm = new Vue()
-
-    it('calls warnHandler if warnHandler is set', () => {
-      const spy = (Vue.config.warnHandler = vi.fn())
-
-      warn(msg, vm)
-
-      expect(spy.mock.calls[0][0]).toBe(msg)
-      expect(spy.mock.calls[0][1]).toBe(vm)
-
-      // @ts-expect-error
-      Vue.config.warnHandler = null
-    })
-
-    it('calls console.error if silent is false', () => {
-      Vue.config.silent = false
-
-      warn(msg, vm)
-
-      expect(msg).toHaveBeenWarned()
-      expect(console.error).toHaveBeenCalled()
-    })
-
-    it('does not call console.error if silent is true', () => {
-      Vue.config.silent = true
-
-      warn(msg, vm)
-
-      expect(console.error).not.toHaveBeenCalled()
-
-      Vue.config.silent = false
-    })
-  })
-})
diff --git a/test/unit/features/directives/bind.spec.ts b/test/unit/features/directives/bind.spec.ts
deleted file mode 100644
index 9adb72ff22a..00000000000
--- a/test/unit/features/directives/bind.spec.ts
+++ /dev/null
@@ -1,631 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-bind', () => {
-  it('normal attr', done => {
-    const vm = new Vue({
-      template: '<div><span :test="foo">hello</span></div>',
-      data: { foo: 'ok' }
-    }).$mount()
-    expect(vm.$el.firstChild.getAttribute('test')).toBe('ok')
-    vm.foo = 'again'
-    waitForUpdate(() => {
-      expect(vm.$el.firstChild.getAttribute('test')).toBe('again')
-      vm.foo = null
-    })
-      .then(() => {
-        expect(vm.$el.firstChild.hasAttribute('test')).toBe(false)
-        vm.foo = false
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.hasAttribute('test')).toBe(false)
-        vm.foo = true
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.getAttribute('test')).toBe('true')
-        vm.foo = 0
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.getAttribute('test')).toBe('0')
-      })
-      .then(done)
-  })
-
-  it('should set property for input value', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <input type="text" :value="foo">
-          <input type="checkbox" :checked="bar">
-        </div>
-      `,
-      data: {
-        foo: 'ok',
-        bar: false
-      }
-    }).$mount()
-    expect(vm.$el.firstChild.value).toBe('ok')
-    expect(vm.$el.lastChild.checked).toBe(false)
-    vm.bar = true
-    waitForUpdate(() => {
-      expect(vm.$el.lastChild.checked).toBe(true)
-    }).then(done)
-  })
-
-  it('xlink', done => {
-    const vm = new Vue({
-      template: '<svg><a :xlink:special="foo"></a></svg>',
-      data: {
-        foo: 'ok'
-      }
-    }).$mount()
-    const xlinkNS = 'http://www.w3.org/1999/xlink'
-    expect(vm.$el.firstChild.getAttributeNS(xlinkNS, 'special')).toBe('ok')
-    vm.foo = 'again'
-    waitForUpdate(() => {
-      expect(vm.$el.firstChild.getAttributeNS(xlinkNS, 'special')).toBe('again')
-      vm.foo = null
-    })
-      .then(() => {
-        expect(vm.$el.firstChild.hasAttributeNS(xlinkNS, 'special')).toBe(false)
-        vm.foo = true
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.getAttributeNS(xlinkNS, 'special')).toBe(
-          'true'
-        )
-      })
-      .then(done)
-  })
-
-  it('enumerated attr', done => {
-    const vm = new Vue({
-      template: '<div><span :contenteditable="foo">hello</span></div>',
-      data: { foo: true }
-    }).$mount()
-    expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('true')
-    vm.foo = 'plaintext-only' // allow special values
-    waitForUpdate(() => {
-      expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe(
-        'plaintext-only'
-      )
-      vm.foo = null
-    })
-      .then(() => {
-        expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('false')
-        vm.foo = ''
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('true')
-        vm.foo = false
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('false')
-        vm.foo = 'false'
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.getAttribute('contenteditable')).toBe('false')
-      })
-      .then(done)
-  })
-
-  it('boolean attr', done => {
-    const vm = new Vue({
-      template: '<div><span :disabled="foo">hello</span></div>',
-      data: { foo: true }
-    }).$mount()
-    expect(vm.$el.firstChild.getAttribute('disabled')).toBe('disabled')
-    vm.foo = 'again'
-    waitForUpdate(() => {
-      expect(vm.$el.firstChild.getAttribute('disabled')).toBe('disabled')
-      vm.foo = null
-    })
-      .then(() => {
-        expect(vm.$el.firstChild.hasAttribute('disabled')).toBe(false)
-        vm.foo = ''
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.hasAttribute('disabled')).toBe(true)
-      })
-      .then(done)
-  })
-
-  it('.prop modifier', () => {
-    const vm = new Vue({
-      template:
-        '<div><span v-bind:text-content.prop="foo"></span><span :inner-html.prop="bar"></span></div>',
-      data: {
-        foo: 'hello',
-        bar: '<span>qux</span>'
-      }
-    }).$mount()
-    expect(vm.$el.children[0].textContent).toBe('hello')
-    expect(vm.$el.children[1].innerHTML).toBe('<span>qux</span>')
-  })
-
-  it('.prop modifier with normal attribute binding', () => {
-    const vm = new Vue({
-      template: '<input :some.prop="some" :id="id">',
-      data: {
-        some: 'hello',
-        id: false
-      }
-    }).$mount()
-    expect(vm.$el.some).toBe('hello')
-    expect(vm.$el.getAttribute('id')).toBe(null)
-  })
-
-  if (process.env.VBIND_PROP_SHORTHAND) {
-    it('.prop modifier shorthand', () => {
-      const vm = new Vue({
-        template:
-          '<div><span .text-content="foo"></span><span .inner-html="bar"></span></div>',
-        data: {
-          foo: 'hello',
-          bar: '<span>qux</span>'
-        }
-      }).$mount()
-      expect(vm.$el.children[0].textContent).toBe('hello')
-      expect(vm.$el.children[1].innerHTML).toBe('<span>qux</span>')
-    })
-  }
-
-  it('.camel modifier', () => {
-    const vm = new Vue({
-      template: '<svg :view-box.camel="viewBox"></svg>',
-      data: {
-        viewBox: '0 0 1 1'
-      }
-    }).$mount()
-    expect(vm.$el.getAttribute('viewBox')).toBe('0 0 1 1')
-  })
-
-  it('.sync modifier', done => {
-    const vm = new Vue({
-      template: `<test :foo-bar.sync="bar"/>`,
-      data: {
-        bar: 1
-      },
-      components: {
-        test: {
-          props: ['fooBar'],
-          template: `<div @click="$emit('update:fooBar', 2)">{{ fooBar }}</div>`
-        }
-      }
-    }).$mount()
-
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.textContent).toBe('1')
-    triggerEvent(vm.$el, 'click')
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('2')
-      document.body.removeChild(vm.$el)
-    }).then(done)
-  })
-
-  it('.sync modifier with kebab case event', done => {
-    const vm = new Vue({
-      template: `<test ref="test" :foo-bar.sync="bar"/>`,
-      data: {
-        bar: 1
-      },
-      components: {
-        test: {
-          props: ['fooBar'],
-          template: `<div>{{ fooBar }}</div>`,
-          methods: {
-            update() {
-              this.$emit('update:foo-bar', 2)
-            }
-          }
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('1')
-    vm.$refs.test.update()
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('2')
-    }).then(done)
-  })
-
-  it('bind object', done => {
-    const vm = new Vue({
-      template: '<input v-bind="test">',
-      data: {
-        test: {
-          id: 'test',
-          class: 'ok',
-          value: 'hello'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.getAttribute('id')).toBe('test')
-    expect(vm.$el.getAttribute('class')).toBe('ok')
-    expect(vm.$el.value).toBe('hello')
-    vm.test.id = 'hi'
-    vm.test.value = 'bye'
-    waitForUpdate(() => {
-      expect(vm.$el.getAttribute('id')).toBe('hi')
-      expect(vm.$el.getAttribute('class')).toBe('ok')
-      expect(vm.$el.value).toBe('bye')
-    }).then(done)
-  })
-
-  it('bind object with explicit overrides', () => {
-    const vm = new Vue({
-      template: `<test v-bind="test" data-foo="foo" dataBar="bar"/>`,
-      components: {
-        test: {
-          template: '<div>{{ dataFoo }} {{ dataBar }}</div>',
-          props: ['dataFoo', 'dataBar']
-        }
-      },
-      data: {
-        test: {
-          dataFoo: 'hi',
-          dataBar: 'bye'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('foo bar')
-  })
-
-  it('.sync modifier with bind object', done => {
-    const vm = new Vue({
-      template: `<test v-bind.sync="test"/>`,
-      data: {
-        test: {
-          fooBar: 1
-        }
-      },
-      components: {
-        test: {
-          props: ['fooBar'],
-          template: `<div @click="handleUpdate">{{ fooBar }}</div>`,
-          methods: {
-            handleUpdate() {
-              this.$emit('update:fooBar', 2)
-            }
-          }
-        }
-      }
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.textContent).toBe('1')
-    triggerEvent(vm.$el, 'click')
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('2')
-      vm.test.fooBar = 3
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('3')
-        document.body.removeChild(vm.$el)
-      })
-      .then(done)
-  })
-
-  it('bind object with overwrite', done => {
-    const vm = new Vue({
-      template: '<input v-bind="test" id="foo" :class="test.value">',
-      data: {
-        test: {
-          id: 'test',
-          class: 'ok',
-          value: 'hello'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.getAttribute('id')).toBe('foo')
-    expect(vm.$el.getAttribute('class')).toBe('hello')
-    expect(vm.$el.value).toBe('hello')
-    vm.test.id = 'hi'
-    vm.test.value = 'bye'
-    waitForUpdate(() => {
-      expect(vm.$el.getAttribute('id')).toBe('foo')
-      expect(vm.$el.getAttribute('class')).toBe('bye')
-      expect(vm.$el.value).toBe('bye')
-    }).then(done)
-  })
-
-  it('bind object with class/style', done => {
-    const vm = new Vue({
-      template: '<input class="a" style="color:red" v-bind="test">',
-      data: {
-        test: {
-          id: 'test',
-          class: ['b', 'c'],
-          style: { fontSize: '12px' }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.id).toBe('test')
-    expect(vm.$el.className).toBe('a b c')
-    expect(vm.$el.style.color).toBe('red')
-    expect(vm.$el.style.fontSize).toBe('12px')
-    vm.test.id = 'hi'
-    vm.test.class = ['d']
-    vm.test.style = { fontSize: '14px' }
-    waitForUpdate(() => {
-      expect(vm.$el.id).toBe('hi')
-      expect(vm.$el.className).toBe('a d')
-      expect(vm.$el.style.color).toBe('red')
-      expect(vm.$el.style.fontSize).toBe('14px')
-    }).then(done)
-  })
-
-  it('bind object as prop', done => {
-    const vm = new Vue({
-      template: '<input v-bind.prop="test">',
-      data: {
-        test: {
-          id: 'test',
-          className: 'ok',
-          value: 'hello'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.id).toBe('test')
-    expect(vm.$el.className).toBe('ok')
-    expect(vm.$el.value).toBe('hello')
-    vm.test.id = 'hi'
-    vm.test.className = 'okay'
-    vm.test.value = 'bye'
-    waitForUpdate(() => {
-      expect(vm.$el.id).toBe('hi')
-      expect(vm.$el.className).toBe('okay')
-      expect(vm.$el.value).toBe('bye')
-    }).then(done)
-  })
-
-  it('bind array', done => {
-    const vm = new Vue({
-      template: '<input v-bind="test">',
-      data: {
-        test: [{ id: 'test', class: 'ok' }, { value: 'hello' }]
-      }
-    }).$mount()
-    expect(vm.$el.getAttribute('id')).toBe('test')
-    expect(vm.$el.getAttribute('class')).toBe('ok')
-    expect(vm.$el.value).toBe('hello')
-    vm.test[0].id = 'hi'
-    vm.test[1].value = 'bye'
-    waitForUpdate(() => {
-      expect(vm.$el.getAttribute('id')).toBe('hi')
-      expect(vm.$el.getAttribute('class')).toBe('ok')
-      expect(vm.$el.value).toBe('bye')
-    }).then(done)
-  })
-
-  it('warn expect object', () => {
-    new Vue({
-      template: '<input v-bind="test">',
-      data: {
-        test: 1
-      }
-    }).$mount()
-    expect(
-      'v-bind without argument expects an Object or Array value'
-    ).toHaveBeenWarned()
-  })
-
-  it('set value for option element', () => {
-    const vm = new Vue({
-      template: '<select><option :value="val">val</option></select>',
-      data: {
-        val: 'val'
-      }
-    }).$mount()
-    // check value attribute
-    expect(vm.$el.options[0].getAttribute('value')).toBe('val')
-  })
-
-  // a vdom patch edge case where the user has several un-keyed elements of the
-  // same tag next to each other, and toggling them.
-  it('properly update for toggling un-keyed children', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <div v-if="ok" id="a" data-test="1"></div>
-          <div v-if="!ok" id="b"></div>
-        </div>
-      `,
-      data: {
-        ok: true
-      }
-    }).$mount()
-    expect(vm.$el.children[0].id).toBe('a')
-    expect(vm.$el.children[0].getAttribute('data-test')).toBe('1')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].id).toBe('b')
-      expect(vm.$el.children[0].getAttribute('data-test')).toBe(null)
-    }).then(done)
-  })
-
-  describe('bind object with special attribute', () => {
-    function makeInstance(options) {
-      return new Vue({
-        template: `<div>${options.parentTemp}</div>`,
-        data: {
-          attrs: {
-            [options.attr]: options.value
-          }
-        },
-        components: {
-          comp: {
-            template: options.childTemp
-          }
-        }
-      }).$mount()
-    }
-
-    it('key', () => {
-      const vm = makeInstance({
-        attr: 'key',
-        value: 'test',
-        parentTemp: '<div v-bind="attrs"></div>'
-      })
-      expect(vm._vnode.children[0].key).toBe('test')
-    })
-
-    it('ref', () => {
-      const vm = makeInstance({
-        attr: 'ref',
-        value: 'test',
-        parentTemp: '<div v-bind="attrs"></div>'
-      })
-      expect(vm.$refs.test).toBe(vm.$el.firstChild)
-    })
-
-    it('slot', () => {
-      const vm = makeInstance({
-        attr: 'slot',
-        value: 'test',
-        parentTemp: '<comp><span v-bind="attrs">123</span></comp>',
-        childTemp: '<div>slot:<slot name="test"></slot></div>'
-      })
-      expect(vm.$el.innerHTML).toBe('<div>slot:<span>123</span></div>')
-    })
-
-    it('is', () => {
-      const vm = makeInstance({
-        attr: 'is',
-        value: 'comp',
-        parentTemp: '<component v-bind="attrs"></component>',
-        childTemp: '<div>comp</div>'
-      })
-      expect(vm.$el.innerHTML).toBe('<div>comp</div>')
-    })
-  })
-
-  describe('dynamic arguments', () => {
-    it('basic', done => {
-      const vm = new Vue({
-        template: `<div v-bind:[key]="value"></div>`,
-        data: {
-          key: 'id',
-          value: 'hello'
-        }
-      }).$mount()
-      expect(vm.$el.id).toBe('hello')
-      vm.key = 'class'
-      waitForUpdate(() => {
-        expect(vm.$el.id).toBe('')
-        expect(vm.$el.className).toBe('hello')
-        // explicit null value
-        vm.key = null
-      })
-        .then(() => {
-          expect(vm.$el.className).toBe('')
-          expect(vm.$el.id).toBe('')
-          vm.key = undefined
-        })
-        .then(() => {
-          expect(
-            `Invalid value for dynamic directive argument`
-          ).toHaveBeenWarned()
-        })
-        .then(done)
-    })
-
-    it('shorthand', done => {
-      const vm = new Vue({
-        template: `<div :[key]="value"></div>`,
-        data: {
-          key: 'id',
-          value: 'hello'
-        }
-      }).$mount()
-      expect(vm.$el.id).toBe('hello')
-      vm.key = 'class'
-      waitForUpdate(() => {
-        expect(vm.$el.className).toBe('hello')
-      }).then(done)
-    })
-
-    it('with .prop modifier', done => {
-      const vm = new Vue({
-        template: `<div :[key].prop="value"></div>`,
-        data: {
-          key: 'id',
-          value: 'hello'
-        }
-      }).$mount()
-      expect(vm.$el.id).toBe('hello')
-      vm.key = 'textContent'
-      waitForUpdate(() => {
-        expect(vm.$el.textContent).toBe('hello')
-      }).then(done)
-    })
-
-    if (process.env.VBIND_PROP_SHORTHAND) {
-      it('.prop shorthand', done => {
-        const vm = new Vue({
-          template: `<div .[key]="value"></div>`,
-          data: {
-            key: 'id',
-            value: 'hello'
-          }
-        }).$mount()
-        expect(vm.$el.id).toBe('hello')
-        vm.key = 'textContent'
-        waitForUpdate(() => {
-          expect(vm.$el.textContent).toBe('hello')
-        }).then(done)
-      })
-    }
-
-    it('handle class and style', () => {
-      const vm = new Vue({
-        template: `<div :[key]="value" :[key2]="value2"></div>`,
-        data: {
-          key: 'class',
-          value: ['hello', 'world'],
-          key2: 'style',
-          value2: {
-            color: 'red'
-          }
-        }
-      }).$mount()
-      expect(vm.$el.className).toBe('hello world')
-      expect(vm.$el.style.color).toBe('red')
-    })
-
-    it('handle shouldUseProp', done => {
-      const vm = new Vue({
-        template: `<input :[key]="value">`,
-        data: {
-          key: 'value',
-          value: 'foo'
-        }
-      }).$mount()
-      expect(vm.$el.value).toBe('foo')
-      vm.value = 'bar'
-      waitForUpdate(() => {
-        expect(vm.$el.value).toBe('bar')
-      }).then(done)
-    })
-
-    it('with .sync modifier', done => {
-      const vm = new Vue({
-        template: `<foo ref="child" :[key].sync="value"/>`,
-        data: {
-          key: 'foo',
-          value: 'bar'
-        },
-        components: {
-          foo: {
-            props: ['foo'],
-            template: `<div>{{ foo }}</div>`
-          }
-        }
-      }).$mount()
-      expect(vm.$el.textContent).toBe('bar')
-      vm.$refs.child.$emit('update:foo', 'baz')
-      waitForUpdate(() => {
-        expect(vm.value).toBe('baz')
-        expect(vm.$el.textContent).toBe('baz')
-      }).then(done)
-    })
-  })
-})
diff --git a/test/unit/features/directives/class.spec.ts b/test/unit/features/directives/class.spec.ts
deleted file mode 100644
index f2c00110b6a..00000000000
--- a/test/unit/features/directives/class.spec.ts
+++ /dev/null
@@ -1,237 +0,0 @@
-import Vue from 'vue'
-import { isFunction } from 'core/util'
-
-function assertClass(assertions, done) {
-  const vm = new Vue({
-    template: '<div class="foo" :class="value"></div>',
-    data: { value: '' }
-  }).$mount()
-  const chain = waitForUpdate()
-  assertions.forEach(([value, expected], i) => {
-    chain
-      .then(() => {
-        if (isFunction(value)) {
-          value(vm.value)
-        } else {
-          vm.value = value
-        }
-      })
-      .then(() => {
-        expect(vm.$el.className).toBe(expected)
-        // NOTE THIS WAS MAKING
-        // if (i >= assertions.length - 1) {
-        //   done()
-        // }
-      })
-  })
-  chain.then(done)
-}
-
-describe('Directive v-bind:class', () => {
-  it('plain string', done => {
-    assertClass(
-      [
-        ['bar', 'foo bar'],
-        ['baz qux', 'foo baz qux'],
-        ['qux', 'foo qux'],
-        [undefined, 'foo']
-      ],
-      done
-    )
-  })
-
-  it('object value', done => {
-    assertClass(
-      [
-        [{ bar: true, baz: false }, 'foo bar'],
-        [{ baz: true }, 'foo baz'],
-        [null, 'foo'],
-        [{ 'bar baz': true, qux: false }, 'foo bar baz'],
-        [{ qux: true }, 'foo qux']
-      ],
-      done
-    )
-  })
-
-  it('array value', done => {
-    assertClass(
-      [
-        [['bar', 'baz'], 'foo bar baz'],
-        [['qux', 'baz'], 'foo qux baz'],
-        [['w', 'x y z'], 'foo w x y z'],
-        [undefined, 'foo'],
-        [['bar'], 'foo bar'],
-        [val => val.push('baz'), 'foo bar baz']
-      ],
-      done
-    )
-  })
-
-  it('array of mixed values', done => {
-    assertClass(
-      [
-        [['x', { y: true, z: true }], 'foo x y z'],
-        [['x', { y: true, z: false }], 'foo x y'],
-        [['f', { z: true }], 'foo f z'],
-        [['l', 'f', { n: true, z: true }], 'foo l f n z'],
-        [['x', {}], 'foo x'],
-        [undefined, 'foo']
-      ],
-      done
-    )
-  })
-
-  it('class merge between parent and child', done => {
-    const vm = new Vue({
-      template: '<child class="a" :class="value"></child>',
-      data: { value: 'b' },
-      components: {
-        child: {
-          template: '<div class="c" :class="value"></div>',
-          data: () => ({ value: 'd' })
-        }
-      }
-    }).$mount()
-    const child = vm.$children[0]
-    expect(vm.$el.className).toBe('c a d b')
-    vm.value = 'e'
-    waitForUpdate(() => {
-      expect(vm.$el.className).toBe('c a d e')
-    })
-      .then(() => {
-        child.value = 'f'
-      })
-      .then(() => {
-        expect(vm.$el.className).toBe('c a f e')
-      })
-      .then(() => {
-        vm.value = { foo: true }
-        child.value = ['bar', 'baz']
-      })
-      .then(() => {
-        expect(vm.$el.className).toBe('c a bar baz foo')
-      })
-      .then(done)
-  })
-
-  it('class merge between multiple nested components sharing same element', done => {
-    const vm = new Vue({
-      template: `
-        <component1 :class="componentClass1">
-          <component2 :class="componentClass2">
-            <component3 :class="componentClass3">
-              some text
-            </component3>
-          </component2>
-        </component1>
-      `,
-      data: {
-        componentClass1: 'componentClass1',
-        componentClass2: 'componentClass2',
-        componentClass3: 'componentClass3'
-      },
-      components: {
-        component1: {
-          render() {
-            return this.$slots.default[0]
-          }
-        },
-        component2: {
-          render() {
-            return this.$slots.default[0]
-          }
-        },
-        component3: {
-          template: '<div class="staticClass"><slot></slot></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.className).toBe(
-      'staticClass componentClass3 componentClass2 componentClass1'
-    )
-    vm.componentClass1 = 'c1'
-    waitForUpdate(() => {
-      expect(vm.$el.className).toBe(
-        'staticClass componentClass3 componentClass2 c1'
-      )
-      vm.componentClass2 = 'c2'
-    })
-      .then(() => {
-        expect(vm.$el.className).toBe('staticClass componentClass3 c2 c1')
-        vm.componentClass3 = 'c3'
-      })
-      .then(() => {
-        expect(vm.$el.className).toBe('staticClass c3 c2 c1')
-      })
-      .then(done)
-  })
-
-  it('deep update', done => {
-    const vm = new Vue({
-      template: '<div :class="test"></div>',
-      data: {
-        test: { a: true, b: false }
-      }
-    }).$mount()
-    expect(vm.$el.className).toBe('a')
-    vm.test.b = true
-    waitForUpdate(() => {
-      expect(vm.$el.className).toBe('a b')
-    }).then(done)
-  })
-
-  // css static classes should only contain a single space in between,
-  // as all the text inside of classes is shipped as a JS string
-  // and this could lead to useless spacing in static classes
-  it('condenses whitespace in staticClass', done => {
-    const vm = new Vue({
-      template:
-        '<div class=" test1\ntest2\ttest3 test4   test5 \n \n \ntest6\t"></div>'
-    }).$mount()
-    expect(vm.$el.className).toBe('test1 test2 test3 test4 test5 test6')
-    done()
-  })
-
-  it('condenses whitespace in staticClass merge in a component', done => {
-    const vm = new Vue({
-      template: `
-        <component1 class="\n\t staticClass \t\n" :class="componentClass1">
-        </component1>
-      `,
-      data: {
-        componentClass1: 'componentClass1'
-      },
-      components: {
-        component1: {
-          template: '<div class="\n\t test \t\n"></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.className).toBe('test staticClass componentClass1')
-    vm.componentClass1 = 'c1'
-    waitForUpdate(() => {
-      expect(vm.$el.className).toBe('test staticClass c1')
-    }).then(done)
-  })
-
-  // a vdom patch edge case where the user has several un-keyed elements of the
-  // same tag next to each other, and toggling them.
-  it('properly remove staticClass for toggling un-keyed children', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <div v-if="ok" class="a"></div>
-          <div v-if="!ok"></div>
-        </div>
-      `,
-      data: {
-        ok: true
-      }
-    }).$mount()
-    expect(vm.$el.children[0].className).toBe('a')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].className).toBe('')
-    }).then(done)
-  })
-})
diff --git a/test/unit/features/directives/cloak.spec.ts b/test/unit/features/directives/cloak.spec.ts
deleted file mode 100644
index 618d21bab62..00000000000
--- a/test/unit/features/directives/cloak.spec.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-cloak', () => {
-  it('should be removed after compile', () => {
-    const el = document.createElement('div')
-    el.setAttribute('v-cloak', '')
-    const vm = new Vue({ el })
-    expect(vm.$el.hasAttribute('v-cloak')).toBe(false)
-  })
-})
diff --git a/test/unit/features/directives/for.spec.ts b/test/unit/features/directives/for.spec.ts
deleted file mode 100644
index 7291bd5d264..00000000000
--- a/test/unit/features/directives/for.spec.ts
+++ /dev/null
@@ -1,885 +0,0 @@
-import Vue from 'vue'
-import { hasSymbol } from 'core/util/env'
-
-describe('Directive v-for', () => {
-  it('should render array of primitive values', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-for="item in list">{{item}}</span>
-        </div>
-      `,
-      data: {
-        list: ['a', 'b', 'c']
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>a</span><span>b</span><span>c</span>')
-    Vue.set(vm.list, 0, 'd')
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>d</span><span>b</span><span>c</span>'
-      )
-      vm.list.push('d')
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>d</span><span>b</span><span>c</span><span>d</span>'
-        )
-        vm.list.splice(1, 2)
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>d</span><span>d</span>')
-        vm.list = ['x', 'y']
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
-      })
-      .then(done)
-  })
-
-  it('should render array of primitive values with index', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-for="(item, i) in list">{{i}}-{{item}}</span>
-        </div>
-      `,
-      data: {
-        list: ['a', 'b', 'c']
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      '<span>0-a</span><span>1-b</span><span>2-c</span>'
-    )
-    Vue.set(vm.list, 0, 'd')
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>0-d</span><span>1-b</span><span>2-c</span>'
-      )
-      vm.list.push('d')
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>0-d</span><span>1-b</span><span>2-c</span><span>3-d</span>'
-        )
-        vm.list.splice(1, 2)
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-d</span>')
-        vm.list = ['x', 'y']
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
-      })
-      .then(done)
-  })
-
-  it('should render array of object values', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-for="item in list">{{item.value}}</span>
-        </div>
-      `,
-      data: {
-        list: [{ value: 'a' }, { value: 'b' }, { value: 'c' }]
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>a</span><span>b</span><span>c</span>')
-    Vue.set(vm.list, 0, { value: 'd' })
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>d</span><span>b</span><span>c</span>'
-      )
-      vm.list[0].value = 'e'
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>e</span><span>b</span><span>c</span>'
-        )
-        vm.list.push({})
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>e</span><span>b</span><span>c</span><span></span>'
-        )
-        vm.list.splice(1, 2)
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>e</span><span></span>')
-        vm.list = [{ value: 'x' }, { value: 'y' }]
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
-      })
-      .then(done)
-  })
-
-  it('should render array of object values with index', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-for="(item, i) in list">{{i}}-{{item.value}}</span>
-        </div>
-      `,
-      data: {
-        list: [{ value: 'a' }, { value: 'b' }, { value: 'c' }]
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      '<span>0-a</span><span>1-b</span><span>2-c</span>'
-    )
-    Vue.set(vm.list, 0, { value: 'd' })
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>0-d</span><span>1-b</span><span>2-c</span>'
-      )
-      vm.list[0].value = 'e'
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>0-e</span><span>1-b</span><span>2-c</span>'
-        )
-        vm.list.push({})
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>0-e</span><span>1-b</span><span>2-c</span><span>3-</span>'
-        )
-        vm.list.splice(1, 2)
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-</span>')
-        vm.list = [{ value: 'x' }, { value: 'y' }]
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
-      })
-      .then(done)
-  })
-
-  if (hasSymbol) {
-    it('should render native iterables (Map)', () => {
-      const vm = new Vue({
-        template: `<div><span v-for="[key, val] in list">{{key}},{{val}}</span></div>`,
-        data: {
-          list: new Map([
-            [1, 'foo'],
-            [2, 'bar']
-          ])
-        }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe(`<span>1,foo</span><span>2,bar</span>`)
-    })
-
-    it('should render native iterables (Set)', () => {
-      const vm = new Vue({
-        template: `<div><span v-for="val in list">{{val}}</span></div>`,
-        data: {
-          list: new Set([1, 2, 3])
-        }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe(
-        `<span>1</span><span>2</span><span>3</span>`
-      )
-    })
-
-    it('should render iterable of primitive values', done => {
-      const iterable = {
-        models: ['a', 'b', 'c'],
-        index: 0,
-        [Symbol.iterator]() {
-          const iterator = {
-            index: 0,
-            models: this.models,
-            next() {
-              if (this.index < this.models.length) {
-                return { value: this.models[this.index++] }
-              } else {
-                return { done: true }
-              }
-            }
-          }
-          return iterator
-        }
-      }
-      const vm = new Vue({
-        template: `
-          <div>
-            <span v-for="item in list">{{item}}</span>
-          </div>
-        `,
-        data: {
-          list: iterable
-        }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe(
-        '<span>a</span><span>b</span><span>c</span>'
-      )
-      Vue.set(vm.list.models, 0, 'd')
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>d</span><span>b</span><span>c</span>'
-        )
-        vm.list.models.push('d')
-      })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<span>d</span><span>b</span><span>c</span><span>d</span>'
-          )
-          vm.list.models.splice(1, 2)
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<span>d</span><span>d</span>')
-          vm.list.models = ['x', 'y']
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
-        })
-        .then(done)
-    })
-
-    it('should render iterable of primitive values with index', done => {
-      const iterable = {
-        models: ['a', 'b', 'c'],
-        index: 0,
-        [Symbol.iterator]() {
-          const iterator = {
-            index: 0,
-            models: this.models,
-            next() {
-              if (this.index < this.models.length) {
-                return { value: this.models[this.index++] }
-              } else {
-                return { done: true }
-              }
-            }
-          }
-          return iterator
-        }
-      }
-
-      const vm = new Vue({
-        template: `
-          <div>
-            <span v-for="(item, i) in list">{{i}}-{{item}}</span>
-          </div>
-        `,
-        data: {
-          list: iterable
-        }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe(
-        '<span>0-a</span><span>1-b</span><span>2-c</span>'
-      )
-      Vue.set(vm.list.models, 0, 'd')
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>0-d</span><span>1-b</span><span>2-c</span>'
-        )
-        vm.list.models.push('d')
-      })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<span>0-d</span><span>1-b</span><span>2-c</span><span>3-d</span>'
-          )
-          vm.list.models.splice(1, 2)
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-d</span>')
-          vm.list.models = ['x', 'y']
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
-        })
-        .then(done)
-    })
-
-    it('should render iterable of object values', done => {
-      const iterable = {
-        models: [{ value: 'a' }, { value: 'b' }, { value: 'c' }],
-        index: 0,
-        [Symbol.iterator]() {
-          const iterator = {
-            index: 0,
-            models: this.models,
-            next() {
-              if (this.index < this.models.length) {
-                return { value: this.models[this.index++] }
-              } else {
-                return { done: true }
-              }
-            }
-          }
-          return iterator
-        }
-      }
-
-      const vm = new Vue({
-        template: `
-          <div>
-            <span v-for="item in list">{{item.value}}</span>
-          </div>
-        `,
-        data: {
-          list: iterable
-        }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe(
-        '<span>a</span><span>b</span><span>c</span>'
-      )
-      Vue.set(vm.list.models, 0, { value: 'd' })
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>d</span><span>b</span><span>c</span>'
-        )
-        vm.list.models[0].value = 'e'
-      })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<span>e</span><span>b</span><span>c</span>'
-          )
-          vm.list.models.push({})
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<span>e</span><span>b</span><span>c</span><span></span>'
-          )
-          vm.list.models.splice(1, 2)
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<span>e</span><span></span>')
-          vm.list.models = [{ value: 'x' }, { value: 'y' }]
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
-        })
-        .then(done)
-    })
-
-    it('should render iterable of object values with index', done => {
-      const iterable = {
-        models: [{ value: 'a' }, { value: 'b' }, { value: 'c' }],
-        index: 0,
-        [Symbol.iterator]() {
-          const iterator = {
-            index: 0,
-            models: this.models,
-            next() {
-              if (this.index < this.models.length) {
-                return { value: this.models[this.index++] }
-              } else {
-                return { done: true }
-              }
-            }
-          }
-          return iterator
-        }
-      }
-
-      const vm = new Vue({
-        template: `
-          <div>
-            <span v-for="(item, i) in list">{{i}}-{{item.value}}</span>
-          </div>
-        `,
-        data: {
-          list: iterable
-        }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe(
-        '<span>0-a</span><span>1-b</span><span>2-c</span>'
-      )
-      Vue.set(vm.list.models, 0, { value: 'd' })
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>0-d</span><span>1-b</span><span>2-c</span>'
-        )
-        vm.list.models[0].value = 'e'
-      })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<span>0-e</span><span>1-b</span><span>2-c</span>'
-          )
-          vm.list.models.push({})
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<span>0-e</span><span>1-b</span><span>2-c</span><span>3-</span>'
-          )
-          vm.list.models.splice(1, 2)
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-</span>')
-          vm.list.models = [{ value: 'x' }, { value: 'y' }]
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
-        })
-        .then(done)
-    })
-  }
-
-  it('should render an Object', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-for="val in obj">{{val}}</span>
-        </div>
-      `,
-      data: {
-        obj: { a: 0, b: 1, c: 2 }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>0</span><span>1</span><span>2</span>')
-    vm.obj.a = 3
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>3</span><span>1</span><span>2</span>'
-      )
-      Vue.set(vm.obj, 'd', 4)
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>3</span><span>1</span><span>2</span><span>4</span>'
-        )
-        Vue.delete(vm.obj, 'a')
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>1</span><span>2</span><span>4</span>'
-        )
-      })
-      .then(done)
-  })
-
-  it('should render an Object with key', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-for="(val, key) in obj">{{val}}-{{key}}</span>
-        </div>
-      `,
-      data: {
-        obj: { a: 0, b: 1, c: 2 }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      '<span>0-a</span><span>1-b</span><span>2-c</span>'
-    )
-    vm.obj.a = 3
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>3-a</span><span>1-b</span><span>2-c</span>'
-      )
-      Vue.set(vm.obj, 'd', 4)
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>3-a</span><span>1-b</span><span>2-c</span><span>4-d</span>'
-        )
-        Vue.delete(vm.obj, 'a')
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>1-b</span><span>2-c</span><span>4-d</span>'
-        )
-      })
-      .then(done)
-  })
-
-  it('should render an Object with key and index', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-for="(val, key, i) in obj">{{val}}-{{key}}-{{i}}</span>
-        </div>
-      `,
-      data: {
-        obj: { a: 0, b: 1, c: 2 }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      '<span>0-a-0</span><span>1-b-1</span><span>2-c-2</span>'
-    )
-    vm.obj.a = 3
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>3-a-0</span><span>1-b-1</span><span>2-c-2</span>'
-      )
-      Vue.set(vm.obj, 'd', 4)
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>3-a-0</span><span>1-b-1</span><span>2-c-2</span><span>4-d-3</span>'
-        )
-        Vue.delete(vm.obj, 'a')
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>1-b-0</span><span>2-c-1</span><span>4-d-2</span>'
-        )
-      })
-      .then(done)
-  })
-
-  it('should render each key of data', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-for="(val, key) in $data">{{val}}-{{key}}</span>
-        </div>
-      `,
-      data: { a: 0, b: 1, c: 2 }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      '<span>0-a</span><span>1-b</span><span>2-c</span>'
-    )
-    vm.a = 3
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>3-a</span><span>1-b</span><span>2-c</span>'
-      )
-    }).then(done)
-  })
-
-  it('check priorities: v-if before v-for', function () {
-    const vm = new Vue({
-      data: {
-        items: [1, 2, 3]
-      },
-      template:
-        '<div><div v-if="item < 3" v-for="item in items">{{item}}</div></div>'
-    }).$mount()
-    expect(vm.$el.textContent).toBe('12')
-  })
-
-  it('check priorities: v-if after v-for', function () {
-    const vm = new Vue({
-      data: {
-        items: [1, 2, 3]
-      },
-      template:
-        '<div><div v-for="item in items" v-if="item < 3">{{item}}</div></div>'
-    }).$mount()
-    expect(vm.$el.textContent).toBe('12')
-  })
-
-  it('range v-for', () => {
-    const vm = new Vue({
-      template: '<div><div v-for="n in 3">{{n}}</div></div>'
-    }).$mount()
-    expect(vm.$el.textContent).toBe('123')
-  })
-
-  it('without key', done => {
-    const vm = new Vue({
-      data: {
-        items: [
-          { id: 1, msg: 'a' },
-          { id: 2, msg: 'b' },
-          { id: 3, msg: 'c' }
-        ]
-      },
-      template: '<div><div v-for="item in items">{{ item.msg }}</div></div>'
-    }).$mount()
-    expect(vm.$el.textContent).toBe('abc')
-    const first = vm.$el.children[0]
-    vm.items.reverse()
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('cba')
-      // assert reusing DOM element in place
-      expect(vm.$el.children[0]).toBe(first)
-    }).then(done)
-  })
-
-  it('with key', done => {
-    const vm = new Vue({
-      data: {
-        items: [
-          { id: 1, msg: 'a' },
-          { id: 2, msg: 'b' },
-          { id: 3, msg: 'c' }
-        ]
-      },
-      template:
-        '<div><div v-for="item in items" :key="item.id">{{ item.msg }}</div></div>'
-    }).$mount()
-    expect(vm.$el.textContent).toBe('abc')
-    const first = vm.$el.children[0]
-    vm.items.reverse()
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('cba')
-      // assert moving DOM element
-      expect(vm.$el.children[0]).not.toBe(first)
-      expect(vm.$el.children[2]).toBe(first)
-    }).then(done)
-  })
-
-  it('nested loops', () => {
-    const vm = new Vue({
-      data: {
-        items: [
-          { items: [{ a: 1 }, { a: 2 }], a: 1 },
-          { items: [{ a: 3 }, { a: 4 }], a: 2 }
-        ]
-      },
-      template:
-        '<div>' +
-        '<div v-for="(item, i) in items">' +
-        '<p v-for="(subItem, j) in item.items">{{j}} {{subItem.a}} {{i}} {{item.a}}</p>' +
-        '</div>' +
-        '</div>'
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      '<div><p>0 1 0 1</p><p>1 2 0 1</p></div>' +
-        '<div><p>0 3 1 2</p><p>1 4 1 2</p></div>'
-    )
-  })
-
-  it('template v-for', done => {
-    const vm = new Vue({
-      data: {
-        list: [{ a: 1 }, { a: 2 }, { a: 3 }]
-      },
-      template:
-        '<div>' +
-        '<template v-for="item in list">' +
-        '<p>{{item.a}}</p>' +
-        '<p>{{item.a + 1}}</p>' +
-        '</template>' +
-        '</div>'
-    }).$mount()
-    assertMarkup()
-    vm.list.reverse()
-    waitForUpdate(() => {
-      assertMarkup()
-      vm.list.splice(1, 1)
-    })
-      .then(() => {
-        assertMarkup()
-        vm.list.splice(1, 0, { a: 2 })
-      })
-      .then(done)
-
-    function assertMarkup() {
-      const markup = vm.list
-        .map(function (item) {
-          return '<p>' + item.a + '</p><p>' + (item.a + 1) + '</p>'
-        })
-        .join('')
-      expect(vm.$el.innerHTML).toBe(markup)
-    }
-  })
-
-  it('component v-for', done => {
-    const vm = new Vue({
-      data: {
-        list: [{ a: 1 }, { a: 2 }, { a: 3 }]
-      },
-      template:
-        '<div>' +
-        '<test v-for="item in list" :msg="item.a" :key="item.a">' +
-        '<span>{{item.a}}</span>' +
-        '</test>' +
-        '</div>',
-      components: {
-        test: {
-          props: ['msg'],
-          template: '<p>{{msg}}<slot></slot></p>'
-        }
-      }
-    }).$mount()
-    assertMarkup()
-    vm.list.reverse()
-    waitForUpdate(() => {
-      assertMarkup()
-      vm.list.splice(1, 1)
-    })
-      .then(() => {
-        assertMarkup()
-        vm.list.splice(1, 0, { a: 2 })
-      })
-      .then(done)
-
-    function assertMarkup() {
-      const markup = vm.list
-        .map(function (item) {
-          return `<p>${item.a}<span>${item.a}</span></p>`
-        })
-        .join('')
-      expect(vm.$el.innerHTML).toBe(markup)
-    }
-  })
-
-  it('dynamic component v-for', done => {
-    const vm = new Vue({
-      data: {
-        list: [{ type: 'one' }, { type: 'two' }]
-      },
-      template:
-        '<div>' +
-        '<component v-for="item in list" :key="item.type" :is="item.type"></component>' +
-        '</div>',
-      components: {
-        one: {
-          template: '<p>One!</p>'
-        },
-        two: {
-          template: '<div>Two!</div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toContain('<p>One!</p><div>Two!</div>')
-    vm.list.reverse()
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toContain('<div>Two!</div><p>One!</p>')
-    }).then(done)
-  })
-
-  it('should warn component v-for without keys', () => {
-    new Vue({
-      template: `<div><test v-for="i in 3"></test></div>`,
-      components: {
-        test: {
-          render() {}
-        }
-      }
-    }).$mount()
-    expect(
-      `<test v-for="i in 3">: component lists rendered with v-for should have explicit keys`
-    ).toHaveBeenTipped()
-  })
-
-  it('multi nested array reactivity', done => {
-    const vm = new Vue({
-      data: {
-        list: [[['foo']]]
-      },
-      template: `
-        <div>
-          <div v-for="i in list">
-            <div v-for="j in i">
-              <div v-for="k in j">
-                {{ k }}
-              </div>
-            </div>
-          </div>
-        </div>
-      `
-    }).$mount()
-    expect(vm.$el.textContent).toMatch(/\s+foo\s+/)
-    vm.list[0][0].push('bar')
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toMatch(/\s+foo\s+bar\s+/)
-    }).then(done)
-  })
-
-  it('should work with strings', done => {
-    const vm = new Vue({
-      data: {
-        text: 'foo'
-      },
-      template: `
-        <div>
-          <span v-for="letter in text">{{ letter }}.</span>
-        </div>
-      `
-    }).$mount()
-    expect(vm.$el.textContent).toMatch('f.o.o.')
-    vm.text += 'bar'
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toMatch('f.o.o.b.a.r.')
-    }).then(done)
-  })
-
-  // #7792
-  it('should work with multiline expressions', () => {
-    const vm = new Vue({
-      data: {
-        a: [1],
-        b: [2]
-      },
-      template: `
-        <div>
-          <span v-for="n in (
-            a.concat(
-              b
-            )
-          )">{{ n }}</span>
-        </div>
-      `
-    }).$mount()
-    expect(vm.$el.textContent).toBe('12')
-  })
-
-  // #9181
-  it('components with v-for and empty list', done => {
-    const vm = new Vue({
-      template:
-        '<div attr>' +
-        '<foo v-for="item in list" :key="item">{{ item }}</foo>' +
-        '</div>',
-      data: {
-        list: undefined
-      },
-      components: {
-        foo: {
-          template: '<div><slot></slot></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('')
-    vm.list = [1, 2, 3]
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
-    }).then(done)
-  })
-
-  it('elements with v-for and empty list', done => {
-    const vm = new Vue({
-      template:
-        '<div attr>' + '<div v-for="item in list">{{ item }}</div>' + '</div>',
-      data: {
-        list: undefined
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('')
-    vm.list = [1, 2, 3]
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
-    }).then(done)
-  })
-
-  const supportsDestructuring = (() => {
-    try {
-      new Function('var { foo } = bar')
-      return true
-    } catch (e) {}
-  })()
-
-  if (supportsDestructuring) {
-    it('should support destructuring syntax in alias position (object)', () => {
-      const vm = new Vue({
-        data: { list: [{ foo: 'hi', bar: 'ho' }] },
-        template:
-          '<div><div v-for="({ foo, bar }, i) in list">{{ foo }} {{ bar }} {{ i }}</div></div>'
-      }).$mount()
-      expect(vm.$el.textContent).toBe('hi ho 0')
-    })
-
-    it('should support destructuring syntax in alias position (array)', () => {
-      const vm = new Vue({
-        data: {
-          list: [
-            [1, 2],
-            [3, 4]
-          ]
-        },
-        template:
-          '<div><div v-for="([ foo, bar ], i) in list">{{ foo }} {{ bar }} {{ i }}</div></div>'
-      }).$mount()
-      expect(vm.$el.textContent).toBe('1 2 03 4 1')
-    })
-  }
-})
diff --git a/test/unit/features/directives/html.spec.ts b/test/unit/features/directives/html.spec.ts
deleted file mode 100644
index 95b125f1188..00000000000
--- a/test/unit/features/directives/html.spec.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-html', () => {
-  it('should render html', () => {
-    const vm = new Vue({
-      template: '<div v-html="a"></div>',
-      data: { a: 'hello' }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('hello')
-  })
-
-  it('should encode html entities', () => {
-    const vm = new Vue({
-      template: '<div v-html="a"></div>',
-      data: { a: '<span>&lt;</span>' }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>&lt;</span>')
-  })
-
-  it('should work inline', () => {
-    const vm = new Vue({
-      template: `<div v-html="'<span>&lt;</span>'"></div>`
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>&lt;</span>')
-  })
-
-  it('should work inline in DOM', () => {
-    const el = document.createElement('div')
-    el.innerHTML = `<div v-html="'<span>&lt;</span>'"></div>`
-    const vm = new Vue({ el })
-    expect(vm.$el.children[0].innerHTML).toBe('<span>&lt;</span>')
-  })
-
-  it('should support all value types', done => {
-    const vm = new Vue({
-      template: '<div v-html="a"></div>',
-      data: { a: false }
-    }).$mount()
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('false')
-      vm.a = []
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('[]')
-        vm.a = {}
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('{}')
-        vm.a = {
-          toString() {
-            return 'foo'
-          }
-        }
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('foo')
-        vm.a = {
-          toJSON() {
-            return { foo: 'bar' }
-          }
-        }
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('{\n  "foo": "bar"\n}')
-        vm.a = 123
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('123')
-        vm.a = 0
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('0')
-        vm.a = ' '
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(' ')
-        vm.a = '    '
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('    ')
-        vm.a = null
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('')
-        vm.a = undefined
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('')
-      })
-      .then(done)
-  })
-})
diff --git a/test/unit/features/directives/if.spec.ts b/test/unit/features/directives/if.spec.ts
deleted file mode 100644
index caaca63f3af..00000000000
--- a/test/unit/features/directives/if.spec.ts
+++ /dev/null
@@ -1,313 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-if', () => {
-  it('should check if value is truthy', () => {
-    const vm = new Vue({
-      template: '<div><span v-if="foo">hello</span></div>',
-      data: { foo: true }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-  })
-
-  it('should check if value is falsy', () => {
-    const vm = new Vue({
-      template: '<div><span v-if="foo">hello</span></div>',
-      data: { foo: false }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<!---->')
-  })
-
-  it('should update if value changed', done => {
-    const vm = new Vue({
-      template: '<div><span v-if="foo">hello</span></div>',
-      data: { foo: true }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-    vm.foo = false
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<!---->')
-      vm.foo = {}
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-        vm.foo = 0
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-        vm.foo = []
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-        vm.foo = null
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-        vm.foo = '0'
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-        vm.foo = undefined
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!---->')
-        vm.foo = 1
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>hello</span>')
-      })
-      .then(done)
-  })
-
-  it('should work well with v-else', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-if="foo">hello</span>
-          <span v-else>bye</span>
-        </div>
-      `,
-      data: { foo: true }
-    }).$mount()
-    expect(vm.$el.innerHTML.trim()).toBe('<span>hello</span>')
-    vm.foo = false
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML.trim()).toBe('<span>bye</span>')
-      vm.foo = {}
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>hello</span>')
-        vm.foo = 0
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>bye</span>')
-        vm.foo = []
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>hello</span>')
-        vm.foo = null
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>bye</span>')
-        vm.foo = '0'
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>hello</span>')
-        vm.foo = undefined
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>bye</span>')
-        vm.foo = 1
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>hello</span>')
-      })
-      .then(done)
-  })
-
-  it('should work well with v-else-if', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-if="foo">hello</span>
-          <span v-else-if="bar">elseif</span>
-          <span v-else>bye</span>
-        </div>
-      `,
-      data: { foo: true, bar: false }
-    }).$mount()
-    expect(vm.$el.innerHTML.trim()).toBe('<span>hello</span>')
-    vm.foo = false
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML.trim()).toBe('<span>bye</span>')
-      vm.bar = true
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>elseif</span>')
-        vm.bar = false
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>bye</span>')
-        vm.foo = true
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>hello</span>')
-        vm.foo = false
-        vm.bar = {}
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>elseif</span>')
-        vm.bar = 0
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>bye</span>')
-        vm.bar = []
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>elseif</span>')
-        vm.bar = null
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>bye</span>')
-        vm.bar = '0'
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>elseif</span>')
-        vm.bar = undefined
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>bye</span>')
-        vm.bar = 1
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe('<span>elseif</span>')
-      })
-      .then(done)
-  })
-
-  it('should work well with v-for', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-for="(item, i) in list" v-if="item.value">{{i}}</span>
-        </div>
-      `,
-      data: {
-        list: [{ value: true }, { value: false }, { value: true }]
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>0</span><!----><span>2</span>')
-    vm.list[0].value = false
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<!----><!----><span>2</span>')
-      vm.list.push({ value: true })
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<!----><!----><span>2</span><span>3</span>'
-        )
-        vm.list.splice(1, 2)
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<!----><span>1</span>')
-      })
-      .then(done)
-  })
-
-  it('should work well with v-for and v-else', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-for="(item, i) in list" v-if="item.value">hello</span>
-          <span v-else>bye</span>
-        </div>
-      `,
-      data: {
-        list: [{ value: true }, { value: false }, { value: true }]
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML.trim()).toBe(
-      '<span>hello</span><span>bye</span><span>hello</span>'
-    )
-    vm.list[0].value = false
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML.trim()).toBe(
-        '<span>bye</span><span>bye</span><span>hello</span>'
-      )
-      vm.list.push({ value: true })
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe(
-          '<span>bye</span><span>bye</span><span>hello</span><span>hello</span>'
-        )
-        vm.list.splice(1, 2)
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML.trim()).toBe(
-          '<span>bye</span><span>hello</span>'
-        )
-      })
-      .then(done)
-  })
-
-  it('should work with v-for on v-else branch', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-if="false">hello</span>
-          <span v-else v-for="item in list">{{ item }}</span>
-        </div>
-      `,
-      data: {
-        list: [1, 2, 3]
-      }
-    }).$mount()
-    expect(vm.$el.textContent.trim()).toBe('123')
-    vm.list.reverse()
-    waitForUpdate(() => {
-      expect(vm.$el.textContent.trim()).toBe('321')
-    }).then(done)
-  })
-
-  it('should work properly on component root', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <test class="test"></test>
-        </div>
-      `,
-      components: {
-        test: {
-          data() {
-            return { ok: true }
-          },
-          template: '<div v-if="ok" id="ok" class="inner">test</div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.children[0].id).toBe('ok')
-    expect(vm.$el.children[0].className).toBe('inner test')
-    vm.$children[0].ok = false
-    waitForUpdate(() => {
-      // attrs / class modules should not attempt to patch the comment node
-      expect(vm.$el.innerHTML).toBe('<!---->')
-      vm.$children[0].ok = true
-    })
-      .then(() => {
-        expect(vm.$el.children[0].id).toBe('ok')
-        expect(vm.$el.children[0].className).toBe('inner test')
-      })
-      .then(done)
-  })
-
-  it('should maintain stable list to avoid unnecessary patches', done => {
-    const created = vi.fn()
-    const destroyed = vi.fn()
-    const vm = new Vue({
-      data: {
-        ok: true
-      },
-      // when the first div is toggled, the second div should be reused
-      // instead of re-created/destroyed
-      template: `
-        <div>
-          <div v-if="ok"></div>
-          <div><test></test></div>
-        </div>
-      `,
-      components: {
-        test: {
-          template: '<div></div>',
-          created,
-          destroyed
-        }
-      }
-    }).$mount()
-
-    expect(created.mock.calls.length).toBe(1)
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(created.mock.calls.length).toBe(1)
-      expect(destroyed).not.toHaveBeenCalled()
-    }).then(done)
-  })
-})
diff --git a/test/unit/features/directives/model-checkbox.spec.ts b/test/unit/features/directives/model-checkbox.spec.ts
deleted file mode 100644
index 212d03990df..00000000000
--- a/test/unit/features/directives/model-checkbox.spec.ts
+++ /dev/null
@@ -1,400 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-model checkbox', () => {
-  it('should work', done => {
-    const vm = new Vue({
-      data: {
-        test: true
-      },
-      template: '<input type="checkbox" v-model="test">'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.checked).toBe(true)
-    vm.test = false
-    waitForUpdate(function () {
-      expect(vm.$el.checked).toBe(false)
-      expect(vm.test).toBe(false)
-      vm.$el.click()
-      expect(vm.$el.checked).toBe(true)
-      expect(vm.test).toBe(true)
-    })
-      .then(() => {
-        document.body.removeChild(vm.$el)
-      })
-      .then(done)
-  })
-
-  it('should respect value bindings', done => {
-    const vm = new Vue({
-      data: {
-        test: 1,
-        a: 1,
-        b: 2
-      },
-      template:
-        '<input type="checkbox" v-model="test" :true-value="a" :false-value="b">'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.checked).toBe(true)
-    vm.$el.click()
-    expect(vm.$el.checked).toBe(false)
-    expect(vm.test).toBe(2)
-    vm.$el.click()
-    expect(vm.$el.checked).toBe(true)
-    expect(vm.test).toBe(1)
-    vm.test = 2
-    waitForUpdate(() => {
-      expect(vm.$el.checked).toBe(false)
-      vm.test = 1
-    })
-      .then(() => {
-        expect(vm.$el.checked).toBe(true)
-        document.body.removeChild(vm.$el)
-      })
-      .then(done)
-  })
-
-  it('bind to Array value', done => {
-    const vm = new Vue({
-      data: {
-        test: ['1']
-      },
-      template: `
-        <div>
-          {{ test }}
-          <input type="checkbox" v-model="test" value="1">
-          <input type="checkbox" v-model="test" value="2">
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.children[0].checked).toBe(true)
-    expect(vm.$el.children[1].checked).toBe(false)
-    vm.$el.children[0].click()
-    waitForUpdate(() => {
-      expect(vm.test.length).toBe(0)
-      vm.$el.children[1].click()
-    })
-      .then(() => {
-        expect(vm.test).toEqual(['2'])
-        vm.$el.children[0].click()
-      })
-      .then(() => {
-        expect(vm.test).toEqual(['2', '1'])
-        vm.test = ['1']
-      })
-      .then(() => {
-        expect(vm.$el.children[0].checked).toBe(true)
-        expect(vm.$el.children[1].checked).toBe(false)
-      })
-      .then(done)
-  })
-
-  it('bind to Array value ignores false-value', done => {
-    const vm = new Vue({
-      data: {
-        test: ['1']
-      },
-      template: `
-        <div>
-          <input type="checkbox" v-model="test" value="1" :false-value="true">
-          <input type="checkbox" v-model="test" value="2" :false-value="true">
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.children[0].checked).toBe(true)
-    expect(vm.$el.children[1].checked).toBe(false)
-    vm.$el.children[0].click()
-    waitForUpdate(() => {
-      expect(vm.test.length).toBe(0)
-      vm.$el.children[1].click()
-    })
-      .then(() => {
-        expect(vm.test).toEqual(['2'])
-        vm.$el.children[0].click()
-      })
-      .then(() => {
-        expect(vm.test).toEqual(['2', '1'])
-        vm.test = ['1']
-      })
-      .then(() => {
-        expect(vm.$el.children[0].checked).toBe(true)
-        expect(vm.$el.children[1].checked).toBe(false)
-      })
-      .then(done)
-  })
-
-  it('bind to Array value with value bindings', done => {
-    const vm = new Vue({
-      data: {
-        test: [1]
-      },
-      template: `
-        <div>
-          <input type="checkbox" v-model="test" :value="1">
-          <input type="checkbox" v-model="test" :value="2">
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.children[0].checked).toBe(true)
-    expect(vm.$el.children[1].checked).toBe(false)
-    vm.$el.children[0].click()
-    waitForUpdate(() => {
-      expect(vm.test.length).toBe(0)
-      vm.$el.children[1].click()
-    })
-      .then(() => {
-        expect(vm.test).toEqual([2])
-        vm.$el.children[0].click()
-      })
-      .then(() => {
-        expect(vm.test).toEqual([2, 1])
-        vm.test = [1]
-      })
-      .then(() => {
-        expect(vm.$el.children[0].checked).toBe(true)
-        expect(vm.$el.children[1].checked).toBe(false)
-      })
-      .then(done)
-  })
-
-  it('bind to Array value with value bindings (object loose equal)', done => {
-    const vm = new Vue({
-      data: {
-        test: [{ a: 1 }]
-      },
-      template: `
-        <div>
-          <input type="checkbox" v-model="test" :value="{ a: 1 }">
-          <input type="checkbox" v-model="test" :value="{ a: 2 }">
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.children[0].checked).toBe(true)
-    expect(vm.$el.children[1].checked).toBe(false)
-    vm.$el.children[0].click()
-    waitForUpdate(() => {
-      expect(vm.test.length).toBe(0)
-      vm.$el.children[1].click()
-    })
-      .then(() => {
-        expect(vm.test).toEqual([{ a: 2 }])
-        vm.$el.children[0].click()
-      })
-      .then(() => {
-        expect(vm.test).toEqual([{ a: 2 }, { a: 1 }])
-        vm.test = [{ a: 1 }]
-      })
-      .then(() => {
-        expect(vm.$el.children[0].checked).toBe(true)
-        expect(vm.$el.children[1].checked).toBe(false)
-      })
-      .then(done)
-  })
-
-  it('bind to Array value with array value bindings (object loose equal)', done => {
-    const vm = new Vue({
-      data: {
-        test: [{ a: 1 }]
-      },
-      template: `
-        <div>
-          <input type="checkbox" v-model="test" :value="{ a: 1 }">
-          <input type="checkbox" v-model="test" :value="[2]">
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.children[0].checked).toBe(true)
-    expect(vm.$el.children[1].checked).toBe(false)
-    vm.$el.children[0].click()
-    waitForUpdate(() => {
-      expect(vm.test.length).toBe(0)
-      vm.$el.children[1].click()
-    })
-      .then(() => {
-        expect(vm.test).toEqual([[2]])
-        vm.$el.children[0].click()
-      })
-      .then(() => {
-        expect(vm.test).toEqual([[2], { a: 1 }])
-        vm.test = [{ a: 1 }]
-      })
-      .then(() => {
-        expect(vm.$el.children[0].checked).toBe(true)
-        expect(vm.$el.children[1].checked).toBe(false)
-      })
-      .then(done)
-  })
-
-  it('.number modifier', () => {
-    const vm = new Vue({
-      data: {
-        test: [],
-        check: true
-      },
-      template: `
-        <div>
-          <input type="checkbox" v-model.number="test" value="1">
-          <input type="checkbox" v-model="test" value="2">
-          <input type="checkbox" v-model.number="check">
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    const checkboxInputs = vm.$el.getElementsByTagName('input')
-    expect(checkboxInputs[0].checked).toBe(false)
-    expect(checkboxInputs[1].checked).toBe(false)
-    expect(checkboxInputs[2].checked).toBe(true)
-    checkboxInputs[0].click()
-    checkboxInputs[1].click()
-    checkboxInputs[2].click()
-    expect(vm.test).toEqual([1, '2'])
-    expect(vm.check).toEqual(false)
-  })
-
-  it('should respect different primitive type value', done => {
-    const vm = new Vue({
-      data: {
-        test: [0]
-      },
-      template:
-        '<div>' +
-        '<input type="checkbox" value="" v-model="test">' +
-        '<input type="checkbox" value="0" v-model="test">' +
-        '<input type="checkbox" value="1" v-model="test">' +
-        '<input type="checkbox" value="false" v-model="test">' +
-        '<input type="checkbox" value="true" v-model="test">' +
-        '</div>'
-    }).$mount()
-    const checkboxInput = vm.$el.children
-    expect(checkboxInput[0].checked).toBe(false)
-    expect(checkboxInput[1].checked).toBe(true)
-    expect(checkboxInput[2].checked).toBe(false)
-    expect(checkboxInput[3].checked).toBe(false)
-    expect(checkboxInput[4].checked).toBe(false)
-    vm.test = [1]
-    waitForUpdate(() => {
-      expect(checkboxInput[0].checked).toBe(false)
-      expect(checkboxInput[1].checked).toBe(false)
-      expect(checkboxInput[2].checked).toBe(true)
-      expect(checkboxInput[3].checked).toBe(false)
-      expect(checkboxInput[4].checked).toBe(false)
-      vm.test = ['']
-    })
-      .then(() => {
-        expect(checkboxInput[0].checked).toBe(true)
-        expect(checkboxInput[1].checked).toBe(false)
-        expect(checkboxInput[2].checked).toBe(false)
-        expect(checkboxInput[3].checked).toBe(false)
-        expect(checkboxInput[4].checked).toBe(false)
-        vm.test = [false]
-      })
-      .then(() => {
-        expect(checkboxInput[0].checked).toBe(false)
-        expect(checkboxInput[1].checked).toBe(false)
-        expect(checkboxInput[2].checked).toBe(false)
-        expect(checkboxInput[3].checked).toBe(true)
-        expect(checkboxInput[4].checked).toBe(false)
-        vm.test = [true]
-      })
-      .then(() => {
-        expect(checkboxInput[0].checked).toBe(false)
-        expect(checkboxInput[1].checked).toBe(false)
-        expect(checkboxInput[2].checked).toBe(false)
-        expect(checkboxInput[3].checked).toBe(false)
-        expect(checkboxInput[4].checked).toBe(true)
-        vm.test = ['', 0, 1, false, true]
-      })
-      .then(() => {
-        expect(checkboxInput[0].checked).toBe(true)
-        expect(checkboxInput[1].checked).toBe(true)
-        expect(checkboxInput[2].checked).toBe(true)
-        expect(checkboxInput[3].checked).toBe(true)
-        expect(checkboxInput[4].checked).toBe(true)
-      })
-      .then(done)
-  })
-
-  // #4521
-  it('should work with click event', done => {
-    const vm = new Vue({
-      data: {
-        num: 1,
-        checked: false
-      },
-      template:
-        '<div @click="add">click {{ num }}<input ref="checkbox" type="checkbox" v-model="checked"/></div>',
-      methods: {
-        add: function () {
-          this.num++
-        }
-      }
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    const checkbox = vm.$refs.checkbox
-    checkbox.click()
-    waitForUpdate(() => {
-      expect(checkbox.checked).toBe(true)
-      expect(vm.num).toBe(2)
-    }).then(done)
-  })
-
-  it('should get updated with model when in focus', done => {
-    const vm = new Vue({
-      data: {
-        a: 2
-      },
-      template: '<input type="checkbox" v-model="a"/>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    vm.$el.click()
-    waitForUpdate(() => {
-      expect(vm.$el.checked).toBe(false)
-      vm.a = 2
-    })
-      .then(() => {
-        expect(vm.$el.checked).toBe(true)
-      })
-      .then(done)
-  })
-
-  it('triggers a watcher when binding to an array value in a checkbox', done => {
-    const vm = new Vue({
-      data: {
-        test: {
-          thing: false,
-          arr: [true]
-        }
-      },
-      template: `
-        <div>
-          <input type="checkbox" v-model="test.arr[0]">
-          <span>{{ test.arr[0] }}</span>
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.children[0].checked).toBe(true)
-    expect(vm.$el.children[1].textContent).toBe('true')
-    vm.$el.children[0].click()
-    expect(vm.$el.children[0].checked).toBe(false)
-    waitForUpdate(() => {
-      expect(vm.$el.children[1].textContent).toBe('false')
-    }).then(done)
-  })
-
-  // #7811
-  it('type should not be overwritten by v-bind', () => {
-    const vm = new Vue({
-      data: {
-        test: true
-      },
-      template: '<input type="checkbox" v-model="test" v-bind="$attrs">'
-    }).$mount()
-    expect(vm.$el.type).toBe('checkbox')
-  })
-})
diff --git a/test/unit/features/directives/model-component.spec.ts b/test/unit/features/directives/model-component.spec.ts
deleted file mode 100644
index 412278bc366..00000000000
--- a/test/unit/features/directives/model-component.spec.ts
+++ /dev/null
@@ -1,245 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-model component', () => {
-  it('should work', done => {
-    const vm = new Vue({
-      data: {
-        msg: 'hello'
-      },
-      template: `
-        <div>
-          <p>{{ msg }}</p>
-          <test v-model="msg"></test>
-        </div>
-      `,
-      components: {
-        test: {
-          props: ['value'],
-          template: `<input :value="value" @input="$emit('input', $event.target.value)">`
-        }
-      }
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    waitForUpdate(() => {
-      const input = vm.$el.querySelector('input')
-      input.value = 'world'
-      triggerEvent(input, 'input')
-    })
-      .then(() => {
-        expect(vm.msg).toEqual('world')
-        expect(vm.$el.querySelector('p').textContent).toEqual('world')
-        vm.msg = 'changed'
-      })
-      .then(() => {
-        expect(vm.$el.querySelector('p').textContent).toEqual('changed')
-        expect(vm.$el.querySelector('input').value).toEqual('changed')
-      })
-      .then(() => {
-        document.body.removeChild(vm.$el)
-      })
-      .then(done)
-  })
-
-  it('should work with native tags with "is"', done => {
-    const vm = new Vue({
-      data: {
-        msg: 'hello'
-      },
-      template: `
-        <div>
-          <p>{{ msg }}</p>
-          <input is="test" v-model="msg">
-        </div>
-      `,
-      components: {
-        test: {
-          props: ['value'],
-          template: `<input :value="value" @input="$emit('input', $event.target.value)">`
-        }
-      }
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    waitForUpdate(() => {
-      const input = vm.$el.querySelector('input')
-      input.value = 'world'
-      triggerEvent(input, 'input')
-    })
-      .then(() => {
-        expect(vm.msg).toEqual('world')
-        expect(vm.$el.querySelector('p').textContent).toEqual('world')
-        vm.msg = 'changed'
-      })
-      .then(() => {
-        expect(vm.$el.querySelector('p').textContent).toEqual('changed')
-        expect(vm.$el.querySelector('input').value).toEqual('changed')
-      })
-      .then(() => {
-        document.body.removeChild(vm.$el)
-      })
-      .then(done)
-  })
-
-  it('should support customization via model option', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        msg: 'hello'
-      },
-      methods: {
-        spy
-      },
-      template: `
-        <div>
-          <p>{{ msg }}</p>
-          <test v-model="msg" @update="spy"></test>
-        </div>
-      `,
-      components: {
-        test: {
-          model: {
-            prop: 'currentValue',
-            event: 'update'
-          },
-          props: ['currentValue'],
-          template: `<input :value="currentValue" @input="$emit('update', $event.target.value)">`
-        }
-      }
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    waitForUpdate(() => {
-      const input = vm.$el.querySelector('input')
-      input.value = 'world'
-      triggerEvent(input, 'input')
-    })
-      .then(() => {
-        expect(vm.msg).toEqual('world')
-        expect(vm.$el.querySelector('p').textContent).toEqual('world')
-        expect(spy).toHaveBeenCalledWith('world')
-        vm.msg = 'changed'
-      })
-      .then(() => {
-        expect(vm.$el.querySelector('p').textContent).toEqual('changed')
-        expect(vm.$el.querySelector('input').value).toEqual('changed')
-      })
-      .then(() => {
-        document.body.removeChild(vm.$el)
-      })
-      .then(done)
-  })
-
-  it('modifier: .number', () => {
-    const vm = new Vue({
-      template: `<div><my-input ref="input" v-model.number="text"></my-input></div>`,
-      data: { text: 'foo' },
-      components: {
-        'my-input': {
-          template: '<input>'
-        }
-      }
-    }).$mount()
-    expect(vm.text).toBe('foo')
-    vm.$refs.input.$emit('input', 'bar')
-    expect(vm.text).toBe('bar')
-    vm.$refs.input.$emit('input', '123')
-    expect(vm.text).toBe(123)
-  })
-
-  it('modifier: .trim', () => {
-    const vm = new Vue({
-      template: `<div><my-input ref="input" v-model.trim="text"></my-input></div>`,
-      data: { text: 'foo' },
-      components: {
-        'my-input': {
-          template: '<input>'
-        }
-      }
-    }).$mount()
-    expect(vm.text).toBe('foo')
-    vm.$refs.input.$emit('input', '  bar  ')
-    expect(vm.text).toBe('bar')
-    vm.$refs.input.$emit('input', '   foo o  ')
-    expect(vm.text).toBe('foo o')
-  })
-
-  // #8436
-  it('should not double transform mode props', () => {
-    const BaseInput = {
-      props: ['value'],
-      render(h) {
-        return h('input', {
-          domProps: {
-            value: this.value
-          },
-          on: {
-            input: e => this.$emit('input', e.target.value)
-          }
-        })
-      }
-    }
-
-    const FunctionalWrapper = {
-      functional: true,
-      render(h, ctx) {
-        return h(BaseInput, ctx.data)
-      }
-    }
-
-    let triggerCount = 0
-
-    const vm = new Vue({
-      components: {
-        FunctionalWrapper
-      },
-      template: `
-        <div>
-          <functional-wrapper v-model="val"/>
-        </div>
-      `,
-      data: {
-        internalVal: ''
-      },
-      computed: {
-        val: {
-          get() {
-            return this.internalVal
-          },
-          set(val) {
-            triggerCount++
-            this.internalVal = val
-          }
-        }
-      }
-    }).$mount()
-
-    document.body.appendChild(vm.$el)
-    triggerEvent(vm.$el.querySelector('input'), 'input')
-    expect(triggerCount).toBe(1)
-    document.body.removeChild(vm.$el)
-  })
-
-  // #9330
-  it('should add value to $attrs if not defined in props', () => {
-    const TestComponent = {
-      inheritAttrs: false,
-      render(h) {
-        return h('div', this.$attrs.value)
-      }
-    }
-
-    const vm = new Vue({
-      components: {
-        TestComponent
-      },
-      template: `
-        <div>
-          <test-component v-model="val"/>
-        </div>
-      `,
-      data: {
-        val: 'foo'
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe('<div>foo</div>')
-  })
-})
diff --git a/test/unit/features/directives/model-dynamic.spec.ts b/test/unit/features/directives/model-dynamic.spec.ts
deleted file mode 100644
index b50a519f905..00000000000
--- a/test/unit/features/directives/model-dynamic.spec.ts
+++ /dev/null
@@ -1,234 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-model dynamic input type', () => {
-  it('should work', done => {
-    const vm = new Vue({
-      data: {
-        inputType: null,
-        test: 'b'
-      },
-      template: `<input :type="inputType" v-model="test">`
-    }).$mount()
-    document.body.appendChild(vm.$el)
-
-    // test text
-    assertInputWorks(vm, 'inputType').then(done)
-  })
-
-  it('with v-if', done => {
-    const vm = new Vue({
-      data: {
-        ok: true,
-        type: null,
-        test: 'b'
-      },
-      template: `<input v-if="ok" :type="type" v-model="test"><div v-else>haha</div>`
-    }).$mount()
-    document.body.appendChild(vm.$el)
-
-    const chain = assertInputWorks(vm)
-      .then(() => {
-        vm.ok = false
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('haha')
-      })
-      .then(() => {
-        // reset
-        vm.ok = true
-        vm.type = null
-        vm.test = 'b'
-      })
-
-    assertInputWorks(vm, chain).then(done)
-  })
-
-  it('with v-else', done => {
-    const data = {
-      ok: true,
-      type: null,
-      test: 'b'
-    }
-    const vm = new Vue({
-      data,
-      template: `<div v-if="ok">haha</div><input v-else :type="type" v-model="test">`
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.textContent).toBe('haha')
-
-    vm.ok = false
-    assertInputWorks(vm).then(done)
-  })
-
-  it('with v-else-if', done => {
-    const vm = new Vue({
-      data: {
-        foo: true,
-        bar: false,
-        type: null,
-        test: 'b'
-      },
-      template: `<div v-if="foo">text</div><input v-else-if="bar" :type="type" v-model="test">`
-    }).$mount()
-    document.body.appendChild(vm.$el)
-
-    const chain = waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('text')
-    })
-      .then(() => {
-        vm.foo = false
-      })
-      .then(() => {
-        expect(vm._vnode.isComment).toBe(true)
-      })
-      .then(() => {
-        vm.bar = true
-      })
-
-    assertInputWorks(vm, chain).then(done)
-  })
-
-  it('with v-for', done => {
-    const vm = new Vue({
-      data: {
-        data: {
-          text: 'foo',
-          checkbox: true
-        },
-        types: ['text', 'checkbox']
-      },
-      template: `<div>
-        <input v-for="type in types" :type="type" v-model="data[type]">
-      </div>`
-    }).$mount()
-    document.body.appendChild(vm.$el)
-
-    let el1 = vm.$el.children[0]
-    expect(el1.type).toBe('text')
-    expect(el1.value).toBe('foo')
-    el1.value = 'bar'
-    triggerEvent(el1, 'input')
-    expect(vm.data.text).toBe('bar')
-
-    let el2 = vm.$el.children[1]
-    expect(el2.type).toBe('checkbox')
-    expect(el2.checked).toBe(true)
-    el2.click()
-    expect(vm.data.checkbox).toBe(false)
-
-    // now in reverse!
-    vm.types.reverse()
-    waitForUpdate(() => {
-      el1 = vm.$el.children[0]
-      expect(el1.type).toBe('checkbox')
-      expect(el1.checked).toBe(false)
-      el1.click()
-      expect(vm.data.checkbox).toBe(true)
-
-      el2 = vm.$el.children[1]
-      expect(el2.type).toBe('text')
-      expect(el2.value).toBe('bar')
-      el2.value = 'foo'
-      triggerEvent(el2, 'input')
-      expect(vm.data.text).toBe('foo')
-    }).then(done)
-  })
-
-  it('with v-bind', done => {
-    const vm = new Vue({
-      data: {
-        data: {
-          text: 'foo',
-          checkbox: true
-        },
-        inputs: [
-          { id: 'one', type: 'text' },
-          { id: 'two', type: 'checkbox' }
-        ]
-      },
-      template: `<div>
-        <input v-for="i in inputs" v-bind="i" v-model="data[i.type]">
-      </div>`
-    }).$mount()
-    document.body.appendChild(vm.$el)
-
-    let el1 = vm.$el.children[0]
-    expect(el1.id).toBe('one')
-    expect(el1.type).toBe('text')
-    expect(el1.value).toBe('foo')
-    el1.value = 'bar'
-    triggerEvent(el1, 'input')
-    expect(vm.data.text).toBe('bar')
-
-    let el2 = vm.$el.children[1]
-    expect(el2.id).toBe('two')
-    expect(el2.type).toBe('checkbox')
-    expect(el2.checked).toBe(true)
-    el2.click()
-    expect(vm.data.checkbox).toBe(false)
-
-    // now in reverse!
-    vm.inputs.reverse()
-    waitForUpdate(() => {
-      el1 = vm.$el.children[0]
-      expect(el1.id).toBe('two')
-      expect(el1.type).toBe('checkbox')
-      expect(el1.checked).toBe(false)
-      el1.click()
-      expect(vm.data.checkbox).toBe(true)
-
-      el2 = vm.$el.children[1]
-      expect(el2.id).toBe('one')
-      expect(el2.type).toBe('text')
-      expect(el2.value).toBe('bar')
-      el2.value = 'foo'
-      triggerEvent(el2, 'input')
-      expect(vm.data.text).toBe('foo')
-    }).then(done)
-  })
-})
-
-function assertInputWorks(vm, type, chain) {
-  if (typeof type !== 'string') {
-    if (!chain) chain = type
-    type = 'type'
-  }
-  if (!chain) chain = waitForUpdate()
-  chain
-    .then(() => {
-      expect(vm.$el.value).toBe('b')
-      vm.test = 'a'
-    })
-    .then(() => {
-      expect(vm.$el.value).toBe('a')
-      vm.$el.value = 'c'
-      triggerEvent(vm.$el, 'input')
-      expect(vm.test).toBe('c')
-    })
-    .then(() => {
-      // change it to password
-      vm[type] = 'password'
-      vm.test = 'b'
-    })
-    .then(() => {
-      expect(vm.$el.type).toBe('password')
-      expect(vm.$el.value).toBe('b')
-      vm.$el.value = 'c'
-      triggerEvent(vm.$el, 'input')
-      expect(vm.test).toBe('c')
-    })
-    .then(() => {
-      // change it to checkbox...
-      vm[type] = 'checkbox'
-    })
-    .then(() => {
-      expect(vm.$el.type).toBe('checkbox')
-      expect(vm.$el.checked).toBe(true)
-    })
-    .then(() => {
-      vm.$el.click()
-      expect(vm.$el.checked).toBe(false)
-      expect(vm.test).toBe(false)
-    })
-  return chain
-}
diff --git a/test/unit/features/directives/model-file.spec.ts b/test/unit/features/directives/model-file.spec.ts
deleted file mode 100644
index 8c92ab72169..00000000000
--- a/test/unit/features/directives/model-file.spec.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-model file', () => {
-  it('warn to use @change instead', () => {
-    new Vue({
-      data: {
-        file: ''
-      },
-      template: '<input v-model="file" type="file">'
-    }).$mount()
-    expect('Use a v-on:change listener instead').toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/directives/model-parse.spec.ts b/test/unit/features/directives/model-parse.spec.ts
deleted file mode 100644
index f9c48a65e5f..00000000000
--- a/test/unit/features/directives/model-parse.spec.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { parseModel } from 'compiler/directives/model'
-
-describe('model expression parser', () => {
-  it('parse single path', () => {
-    const res = parseModel('foo')
-    expect(res.exp).toBe('foo')
-    expect(res.key).toBe(null)
-  })
-
-  it('parse object dot notation', () => {
-    const res = parseModel('a.b.c')
-    expect(res.exp).toBe('a.b')
-    expect(res.key).toBe('"c"')
-  })
-
-  it('parse string in brackets', () => {
-    const res = parseModel('a["b"][c]')
-    expect(res.exp).toBe('a["b"]')
-    expect(res.key).toBe('c')
-  })
-
-  it('parse brackets with object dot notation', () => {
-    const res = parseModel('a["b"][c].xxx')
-    expect(res.exp).toBe('a["b"][c]')
-    expect(res.key).toBe('"xxx"')
-  })
-
-  it('parse nested brackets', () => {
-    const res = parseModel('a[i[c]]')
-    expect(res.exp).toBe('a')
-    expect(res.key).toBe('i[c]')
-  })
-
-  it('combined', () => {
-    const res = parseModel('test.xxx.a["asa"][test1[key]]')
-    expect(res.exp).toBe('test.xxx.a["asa"]')
-    expect(res.key).toBe('test1[key]')
-  })
-})
diff --git a/test/unit/features/directives/model-radio.spec.ts b/test/unit/features/directives/model-radio.spec.ts
deleted file mode 100644
index f36167a7a7f..00000000000
--- a/test/unit/features/directives/model-radio.spec.ts
+++ /dev/null
@@ -1,269 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-model radio', () => {
-  it('should work', done => {
-    const vm = new Vue({
-      data: {
-        test: '1'
-      },
-      template: `
-        <div>
-          <input type="radio" value="1" v-model="test" name="test">
-          <input type="radio" value="2" v-model="test" name="test">
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.children[0].checked).toBe(true)
-    expect(vm.$el.children[1].checked).toBe(false)
-    vm.test = '2'
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].checked).toBe(false)
-      expect(vm.$el.children[1].checked).toBe(true)
-      vm.$el.children[0].click()
-      expect(vm.$el.children[0].checked).toBe(true)
-      expect(vm.$el.children[1].checked).toBe(false)
-      expect(vm.test).toBe('1')
-    })
-      .then(() => {
-        document.body.removeChild(vm.$el)
-      })
-      .then(done)
-  })
-
-  it('should respect value bindings', done => {
-    const vm = new Vue({
-      data: {
-        test: 1
-      },
-      template: `
-        <div>
-          <input type="radio" :value="1" v-model="test" name="test">
-          <input type="radio" :value="2" v-model="test" name="test">
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.children[0].checked).toBe(true)
-    expect(vm.$el.children[1].checked).toBe(false)
-    vm.test = 2
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].checked).toBe(false)
-      expect(vm.$el.children[1].checked).toBe(true)
-      vm.$el.children[0].click()
-      expect(vm.$el.children[0].checked).toBe(true)
-      expect(vm.$el.children[1].checked).toBe(false)
-      expect(vm.test).toBe(1)
-    })
-      .then(() => {
-        document.body.removeChild(vm.$el)
-      })
-      .then(done)
-  })
-
-  it('should respect value bindings (object loose equal)', done => {
-    const vm = new Vue({
-      data: {
-        test: { a: 1 }
-      },
-      template: `
-        <div>
-          <input type="radio" :value="{ a: 1 }" v-model="test" name="test">
-          <input type="radio" :value="{ a: 2 }" v-model="test" name="test">
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.children[0].checked).toBe(true)
-    expect(vm.$el.children[1].checked).toBe(false)
-    vm.test = { a: 2 }
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].checked).toBe(false)
-      expect(vm.$el.children[1].checked).toBe(true)
-      vm.$el.children[0].click()
-      expect(vm.$el.children[0].checked).toBe(true)
-      expect(vm.$el.children[1].checked).toBe(false)
-      expect(vm.test).toEqual({ a: 1 })
-    })
-      .then(() => {
-        document.body.removeChild(vm.$el)
-      })
-      .then(done)
-  })
-
-  it('multiple radios ', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        selections: ['a', '1'],
-        radioList: [
-          {
-            name: 'questionA',
-            data: ['a', 'b', 'c']
-          },
-          {
-            name: 'questionB',
-            data: ['1', '2']
-          }
-        ]
-      },
-      watch: {
-        selections: spy
-      },
-      template:
-        '<div>' +
-        '<div v-for="(radioGroup, idx) in radioList">' +
-        '<div>' +
-        '<span v-for="(item, index) in radioGroup.data">' +
-        '<input :name="radioGroup.name" type="radio" :value="item" v-model="selections[idx]" :id="idx"/>' +
-        '<label>{{item}}</label>' +
-        '</span>' +
-        '</div>' +
-        '</div>' +
-        '</div>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    const inputs = vm.$el.getElementsByTagName('input')
-    inputs[1].click()
-    waitForUpdate(() => {
-      expect(vm.selections).toEqual(['b', '1'])
-      expect(spy).toHaveBeenCalled()
-    }).then(done)
-  })
-
-  it('.number modifier', () => {
-    const vm = new Vue({
-      data: {
-        test: 1
-      },
-      template: `
-        <div>
-          <input type="radio" value="1" v-model="test" name="test">
-          <input type="radio" value="2" v-model.number="test" name="test">
-        </div>
-      `
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.children[0].checked).toBe(true)
-    expect(vm.$el.children[1].checked).toBe(false)
-    vm.$el.children[1].click()
-    expect(vm.$el.children[0].checked).toBe(false)
-    expect(vm.$el.children[1].checked).toBe(true)
-    expect(vm.test).toBe(2)
-  })
-
-  it('should respect different primitive type value', done => {
-    const vm = new Vue({
-      data: {
-        test: 1
-      },
-      template:
-        '<div>' +
-        '<input type="radio" value="" v-model="test" name="test">' +
-        '<input type="radio" value="0" v-model="test" name="test">' +
-        '<input type="radio" value="1" v-model="test" name="test">' +
-        '<input type="radio" value="false" v-model="test" name="test">' +
-        '<input type="radio" value="true" v-model="test" name="test">' +
-        '</div>'
-    }).$mount()
-    const radioboxInput = vm.$el.children
-    expect(radioboxInput[0].checked).toBe(false)
-    expect(radioboxInput[1].checked).toBe(false)
-    expect(radioboxInput[2].checked).toBe(true)
-    expect(radioboxInput[3].checked).toBe(false)
-    expect(radioboxInput[4].checked).toBe(false)
-    vm.test = 0
-    waitForUpdate(() => {
-      expect(radioboxInput[0].checked).toBe(false)
-      expect(radioboxInput[1].checked).toBe(true)
-      expect(radioboxInput[2].checked).toBe(false)
-      expect(radioboxInput[3].checked).toBe(false)
-      expect(radioboxInput[4].checked).toBe(false)
-      vm.test = ''
-    })
-      .then(() => {
-        expect(radioboxInput[0].checked).toBe(true)
-        expect(radioboxInput[1].checked).toBe(false)
-        expect(radioboxInput[2].checked).toBe(false)
-        expect(radioboxInput[3].checked).toBe(false)
-        expect(radioboxInput[4].checked).toBe(false)
-        vm.test = false
-      })
-      .then(() => {
-        expect(radioboxInput[0].checked).toBe(false)
-        expect(radioboxInput[1].checked).toBe(false)
-        expect(radioboxInput[2].checked).toBe(false)
-        expect(radioboxInput[3].checked).toBe(true)
-        expect(radioboxInput[4].checked).toBe(false)
-        vm.test = true
-      })
-      .then(() => {
-        expect(radioboxInput[0].checked).toBe(false)
-        expect(radioboxInput[1].checked).toBe(false)
-        expect(radioboxInput[2].checked).toBe(false)
-        expect(radioboxInput[3].checked).toBe(false)
-        expect(radioboxInput[4].checked).toBe(true)
-      })
-      .then(done)
-  })
-
-  // #4521
-  it('should work with click event', done => {
-    const vm = new Vue({
-      data: {
-        num: 1,
-        checked: 1
-      },
-      template:
-        '<div @click="add">' +
-        'click {{ num }}<input name="test" type="radio" value="1" v-model="checked"/>' +
-        '<input name="test" type="radio" value="2" v-model="checked"/>' +
-        '</div>',
-      methods: {
-        add: function () {
-          this.num++
-        }
-      }
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    const radios = vm.$el.getElementsByTagName('input')
-    radios[0].click()
-    waitForUpdate(() => {
-      expect(radios[0].checked).toBe(true)
-      expect(radios[1].checked).toBe(false)
-      expect(vm.num).toBe(2)
-      radios[0].click()
-    })
-      .then(() => {
-        expect(radios[0].checked).toBe(true)
-        expect(radios[1].checked).toBe(false)
-        expect(vm.num).toBe(3)
-        radios[1].click()
-      })
-      .then(() => {
-        expect(radios[0].checked).toBe(false)
-        expect(radios[1].checked).toBe(true)
-        expect(vm.num).toBe(4)
-      })
-      .then(done)
-  })
-
-  it('should get updated with model when in focus', done => {
-    const vm = new Vue({
-      data: {
-        a: '2'
-      },
-      template: '<input type="radio" value="1" v-model="a"/>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    vm.$el.click()
-    waitForUpdate(() => {
-      expect(vm.$el.checked).toBe(true)
-      vm.a = 2
-    })
-      .then(() => {
-        expect(vm.$el.checked).toBe(false)
-      })
-      .then(done)
-  })
-})
diff --git a/test/unit/features/directives/model-select.spec.ts b/test/unit/features/directives/model-select.spec.ts
deleted file mode 100644
index 6b9b682f7c9..00000000000
--- a/test/unit/features/directives/model-select.spec.ts
+++ /dev/null
@@ -1,623 +0,0 @@
-import Vue from 'vue'
-import { looseEqual } from 'shared/util'
-
-// Android 4.4 Chrome 30 has the bug that a multi-select option cannot be
-// deselected by setting its "selected" prop via JavaScript.
-function hasMultiSelectBug() {
-  const s = document.createElement('select')
-  s.setAttribute('multiple', '')
-  const o = document.createElement('option')
-  s.appendChild(o)
-  o.selected = true
-  o.selected = false
-  return o.selected !== false
-}
-
-/**
- * setting <select>'s value in IE9 doesn't work
- * we have to manually loop through the options
- */
-function updateSelect(el, value) {
-  const options = el.options
-  let i = options.length
-  while (i--) {
-    if (looseEqual(getValue(options[i]), value)) {
-      options[i].selected = true
-      break
-    }
-  }
-}
-
-function getValue(option) {
-  return '_value' in option ? option._value : option.value || option.text
-}
-
-describe('Directive v-model select', () => {
-  it('should work', done => {
-    const vm = new Vue({
-      data: {
-        test: 'b'
-      },
-      template:
-        '<select v-model="test">' +
-        '<option>a</option>' +
-        '<option>b</option>' +
-        '<option>c</option>' +
-        '</select>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.test).toBe('b')
-    expect(vm.$el.value).toBe('b')
-    expect(vm.$el.childNodes[1].selected).toBe(true)
-    vm.test = 'c'
-    waitForUpdate(function () {
-      expect(vm.$el.value).toBe('c')
-      expect(vm.$el.childNodes[2].selected).toBe(true)
-      updateSelect(vm.$el, 'a')
-      triggerEvent(vm.$el, 'change')
-      expect(vm.test).toBe('a')
-    }).then(done)
-  })
-
-  it('should work with value bindings', done => {
-    const vm = new Vue({
-      data: {
-        test: 2
-      },
-      template:
-        '<select v-model="test">' +
-        '<option value="1">a</option>' +
-        '<option :value="2">b</option>' +
-        '<option :value="3">c</option>' +
-        '</select>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.value).toBe('2')
-    expect(vm.$el.childNodes[1].selected).toBe(true)
-    vm.test = 3
-    waitForUpdate(function () {
-      expect(vm.$el.value).toBe('3')
-      expect(vm.$el.childNodes[2].selected).toBe(true)
-
-      updateSelect(vm.$el, '1')
-      triggerEvent(vm.$el, 'change')
-      expect(vm.test).toBe('1')
-
-      updateSelect(vm.$el, '2')
-      triggerEvent(vm.$el, 'change')
-      expect(vm.test).toBe(2)
-    }).then(done)
-  })
-
-  it('should work with value bindings (object loose equal)', done => {
-    const vm = new Vue({
-      data: {
-        test: { a: 2 }
-      },
-      template:
-        '<select v-model="test">' +
-        '<option value="1">a</option>' +
-        '<option :value="{ a: 2 }">b</option>' +
-        '<option :value="{ a: 3 }">c</option>' +
-        '</select>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.childNodes[1].selected).toBe(true)
-    vm.test = { a: 3 }
-    waitForUpdate(function () {
-      expect(vm.$el.childNodes[2].selected).toBe(true)
-
-      updateSelect(vm.$el, '1')
-      triggerEvent(vm.$el, 'change')
-      expect(vm.test).toBe('1')
-
-      updateSelect(vm.$el, { a: 2 })
-      triggerEvent(vm.$el, 'change')
-      expect(vm.test).toEqual({ a: 2 })
-    }).then(done)
-  })
-
-  it('should work with value bindings (Array loose equal)', done => {
-    const vm = new Vue({
-      data: {
-        test: [{ a: 2 }]
-      },
-      template:
-        '<select v-model="test">' +
-        '<option value="1">a</option>' +
-        '<option :value="[{ a: 2 }]">b</option>' +
-        '<option :value="[{ a: 3 }]">c</option>' +
-        '</select>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.childNodes[1].selected).toBe(true)
-    vm.test = [{ a: 3 }]
-    waitForUpdate(function () {
-      expect(vm.$el.childNodes[2].selected).toBe(true)
-
-      updateSelect(vm.$el, '1')
-      triggerEvent(vm.$el, 'change')
-      expect(vm.test).toBe('1')
-
-      updateSelect(vm.$el, [{ a: 2 }])
-      triggerEvent(vm.$el, 'change')
-      expect(vm.test).toEqual([{ a: 2 }])
-    }).then(done)
-  })
-
-  it('should work with v-for', done => {
-    const vm = new Vue({
-      data: {
-        test: 'b',
-        opts: ['a', 'b', 'c']
-      },
-      template:
-        '<select v-model="test">' +
-        '<option v-for="o in opts">{{ o }}</option>' +
-        '</select>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.test).toBe('b')
-    expect(vm.$el.value).toBe('b')
-    expect(vm.$el.childNodes[1].selected).toBe(true)
-    vm.test = 'c'
-    waitForUpdate(function () {
-      expect(vm.$el.value).toBe('c')
-      expect(vm.$el.childNodes[2].selected).toBe(true)
-      updateSelect(vm.$el, 'a')
-      triggerEvent(vm.$el, 'change')
-      expect(vm.test).toBe('a')
-      // update v-for opts
-      vm.opts = ['d', 'a']
-    })
-      .then(() => {
-        expect(vm.$el.childNodes[0].selected).toBe(false)
-        expect(vm.$el.childNodes[1].selected).toBe(true)
-      })
-      .then(done)
-  })
-
-  it('should work with v-for & value bindings', done => {
-    const vm = new Vue({
-      data: {
-        test: 2,
-        opts: [1, 2, 3]
-      },
-      template:
-        '<select v-model="test">' +
-        '<option v-for="o in opts" :value="o">option {{ o }}</option>' +
-        '</select>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.$el.value).toBe('2')
-    expect(vm.$el.childNodes[1].selected).toBe(true)
-    vm.test = 3
-    waitForUpdate(function () {
-      expect(vm.$el.value).toBe('3')
-      expect(vm.$el.childNodes[2].selected).toBe(true)
-      updateSelect(vm.$el, 1)
-      triggerEvent(vm.$el, 'change')
-      expect(vm.test).toBe(1)
-      // update v-for opts
-      vm.opts = [0, 1]
-    })
-      .then(() => {
-        expect(vm.$el.childNodes[0].selected).toBe(false)
-        expect(vm.$el.childNodes[1].selected).toBe(true)
-      })
-      .then(done)
-  })
-
-  it('should work with select which has no default selected options', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        id: 4,
-        list: [1, 2, 3],
-        testChange: 5
-      },
-      template:
-        '<div>' +
-        '<select @change="test" v-model="id">' +
-        '<option v-for="item in list" :value="item">{{item}}</option>' +
-        '</select>' +
-        '{{testChange}}' +
-        '</div>',
-      methods: {
-        test: spy
-      }
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    vm.testChange = 10
-    waitForUpdate(() => {
-      expect(spy.mock.calls.length).toBe(0)
-    }).then(done)
-  })
-
-  if (!hasMultiSelectBug()) {
-    it('multiple', done => {
-      const vm = new Vue({
-        data: {
-          test: ['b']
-        },
-        template:
-          '<select v-model="test" multiple>' +
-          '<option>a</option>' +
-          '<option>b</option>' +
-          '<option>c</option>' +
-          '</select>'
-      }).$mount()
-      const opts = vm.$el.options
-      expect(opts[0].selected).toBe(false)
-      expect(opts[1].selected).toBe(true)
-      expect(opts[2].selected).toBe(false)
-      vm.test = ['a', 'c']
-      waitForUpdate(() => {
-        expect(opts[0].selected).toBe(true)
-        expect(opts[1].selected).toBe(false)
-        expect(opts[2].selected).toBe(true)
-        opts[0].selected = false
-        opts[1].selected = true
-        triggerEvent(vm.$el, 'change')
-        expect(vm.test).toEqual(['b', 'c'])
-      }).then(done)
-    })
-
-    it('multiple + v-for', done => {
-      const vm = new Vue({
-        data: {
-          test: ['b'],
-          opts: ['a', 'b', 'c']
-        },
-        template:
-          '<select v-model="test" multiple>' +
-          '<option v-for="o in opts">{{ o }}</option>' +
-          '</select>'
-      }).$mount()
-      const opts = vm.$el.options
-      expect(opts[0].selected).toBe(false)
-      expect(opts[1].selected).toBe(true)
-      expect(opts[2].selected).toBe(false)
-      vm.test = ['a', 'c']
-      waitForUpdate(() => {
-        expect(opts[0].selected).toBe(true)
-        expect(opts[1].selected).toBe(false)
-        expect(opts[2].selected).toBe(true)
-        opts[0].selected = false
-        opts[1].selected = true
-        triggerEvent(vm.$el, 'change')
-        expect(vm.test).toEqual(['b', 'c'])
-        // update v-for opts
-        vm.opts = ['c', 'd']
-      })
-        .then(() => {
-          expect(opts[0].selected).toBe(true)
-          expect(opts[1].selected).toBe(false)
-          expect(vm.test).toEqual(['c']) // should remove 'd' which no longer has a matching option
-        })
-        .then(done)
-    })
-  }
-
-  it('should work with multiple binding', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        isMultiple: true,
-        selections: ['1']
-      },
-      template:
-        '<select v-model="selections" :multiple="isMultiple">' +
-        '<option value="1">item 1</option>' +
-        '<option value="2">item 2</option>' +
-        '</select>',
-      watch: {
-        selections: spy
-      }
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    vm.$el.options[1].selected = true
-    triggerEvent(vm.$el, 'change')
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalled()
-      expect(vm.selections).toEqual(['1', '2'])
-    }).then(done)
-  })
-
-  it("should not have multiple attr with falsy values except ''", () => {
-    const vm = new Vue({
-      template:
-        '<div>' +
-        '<select id="undefined" :multiple="undefined"></select>' +
-        '<select id="null" :multiple="null"></select>' +
-        '<select id="false" :multiple="false"></select>' +
-        '<select id="string" :multiple="\'\'"></select>' +
-        '</div>'
-    }).$mount()
-    expect(vm.$el.querySelector('#undefined').multiple).toEqual(false)
-    expect(vm.$el.querySelector('#null').multiple).toEqual(false)
-    expect(vm.$el.querySelector('#false').multiple).toEqual(false)
-    expect(vm.$el.querySelector('#string').multiple).toEqual(true)
-  })
-
-  it('multiple with static template', () => {
-    const vm = new Vue({
-      template:
-        '<select multiple>' +
-        '<option selected>a</option>' +
-        '<option selected>b</option>' +
-        '<option selected>c</option>' +
-        '</select>'
-    }).$mount()
-    const opts = vm.$el.options
-    expect(opts[0].selected).toBe(true)
-    expect(opts[1].selected).toBe(true)
-    expect(opts[2].selected).toBe(true)
-  })
-
-  it('multiple selects', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        selections: ['', ''],
-        selectBoxes: [
-          [
-            { value: 'foo', text: 'foo' },
-            { value: 'bar', text: 'bar' }
-          ],
-          [
-            { value: 'day', text: 'day' },
-            { value: 'night', text: 'night' }
-          ]
-        ]
-      },
-      watch: {
-        selections: spy
-      },
-      template:
-        '<div>' +
-        '<select v-for="(item, index) in selectBoxes" v-model="selections[index]">' +
-        '<option v-for="element in item" v-bind:value="element.value" v-text="element.text"></option>' +
-        '</select>' +
-        '<span ref="rs">{{selections}}</span>' +
-        '</div>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    const selects = vm.$el.getElementsByTagName('select')
-    const select0 = selects[0]
-    select0.options[0].selected = true
-    triggerEvent(select0, 'change')
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalled()
-      expect(vm.selections).toEqual(['foo', ''])
-    }).then(done)
-  })
-
-  it('.number modifier', () => {
-    const vm = new Vue({
-      data: {
-        test: 2
-      },
-      template:
-        '<select v-model.number="test">' +
-        '<option value="1">a</option>' +
-        '<option :value="2">b</option>' +
-        '<option :value="3">c</option>' +
-        '</select>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    updateSelect(vm.$el, '1')
-    triggerEvent(vm.$el, 'change')
-    expect(vm.test).toBe(1)
-  })
-
-  it('should respect different primitive type value', done => {
-    const vm = new Vue({
-      data: {
-        test: 0
-      },
-      template:
-        '<select v-model.number="test">' +
-        '<option value="">a</option>' +
-        '<option value="0">b</option>' +
-        '<option value="1">c</option>' +
-        '<option value="false">c</option>' +
-        '<option value="true">c</option>' +
-        '</select>'
-    }).$mount()
-    const opts = vm.$el.options
-    expect(opts[0].selected).toBe(false)
-    expect(opts[1].selected).toBe(true)
-    expect(opts[2].selected).toBe(false)
-    expect(opts[3].selected).toBe(false)
-    expect(opts[4].selected).toBe(false)
-    vm.test = 1
-    waitForUpdate(() => {
-      expect(opts[0].selected).toBe(false)
-      expect(opts[1].selected).toBe(false)
-      expect(opts[2].selected).toBe(true)
-      expect(opts[3].selected).toBe(false)
-      expect(opts[4].selected).toBe(false)
-      vm.test = ''
-    })
-      .then(() => {
-        expect(opts[0].selected).toBe(true)
-        expect(opts[1].selected).toBe(false)
-        expect(opts[2].selected).toBe(false)
-        expect(opts[3].selected).toBe(false)
-        expect(opts[4].selected).toBe(false)
-        vm.test = false
-      })
-      .then(() => {
-        expect(opts[0].selected).toBe(false)
-        expect(opts[1].selected).toBe(false)
-        expect(opts[2].selected).toBe(false)
-        expect(opts[3].selected).toBe(true)
-        expect(opts[4].selected).toBe(false)
-        vm.test = true
-      })
-      .then(() => {
-        expect(opts[0].selected).toBe(false)
-        expect(opts[1].selected).toBe(false)
-        expect(opts[2].selected).toBe(false)
-        expect(opts[3].selected).toBe(false)
-        expect(opts[4].selected).toBe(true)
-      })
-      .then(done)
-  })
-
-  it('should warn multiple with non-Array value', done => {
-    new Vue({
-      data: {
-        test: 'meh'
-      },
-      template: '<select v-model="test" multiple></select>'
-    }).$mount()
-    // IE warns on a setTimeout as well
-    setTimeout(() => {
-      expect(
-        '<select multiple v-model="test"> expects an Array value for its binding, but got String'
-      ).toHaveBeenWarned()
-      done()
-    }, 0)
-  })
-
-  it('should work with option value that has circular reference', done => {
-    const circular = {}
-    circular.self = circular
-
-    const vm = new Vue({
-      data: {
-        test: 'b',
-        circular
-      },
-      template:
-        '<select v-model="test">' +
-        '<option :value="circular">a</option>' +
-        '<option>b</option>' +
-        '<option>c</option>' +
-        '</select>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    expect(vm.test).toBe('b')
-    expect(vm.$el.value).toBe('b')
-    expect(vm.$el.childNodes[1].selected).toBe(true)
-    vm.test = circular
-    waitForUpdate(function () {
-      expect(vm.$el.childNodes[0].selected).toBe(true)
-    }).then(done)
-  })
-
-  // #6112
-  it('should not set non-matching value to undefined if options did not change', done => {
-    const vm = new Vue({
-      data: {
-        test: '1'
-      },
-      template: '<select v-model="test">' + '<option>a</option>' + '</select>'
-    }).$mount()
-
-    vm.test = '2'
-    waitForUpdate(() => {
-      expect(vm.test).toBe('2')
-    }).then(done)
-  })
-
-  // #6193
-  it('should not trigger change event when matching option can be found for each value', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        options: ['1']
-      },
-      computed: {
-        test: {
-          get() {
-            return '1'
-          },
-          set() {
-            spy()
-          }
-        }
-      },
-      template:
-        '<select v-model="test">' +
-        '<option :key="opt" v-for="opt in options" :value="opt">{{ opt }}</option>' +
-        '</select>'
-    }).$mount()
-
-    vm.options = ['1', '2']
-    waitForUpdate(() => {
-      expect(spy).not.toHaveBeenCalled()
-    }).then(done)
-  })
-
-  // #6903
-  describe('should correctly handle v-model when the vnodes are the same', () => {
-    function makeInstance(foo) {
-      return new Vue({
-        data: {
-          foo: foo,
-          options: ['b', 'c', 'd'],
-          value: 'c'
-        },
-        template:
-          '<div>' +
-          '<select v-if="foo" data-attr>' +
-          '<option selected>a</option>' +
-          '</select>' +
-          '<select v-else v-model="value">' +
-          '<option v-for="option in options" :value="option">{{ option }}</option>' +
-          '</select>' +
-          '</div>'
-      }).$mount()
-    }
-
-    it('register v-model', done => {
-      const vm = makeInstance(true)
-
-      expect(vm.$el.firstChild.selectedIndex).toBe(0)
-      vm.foo = false
-      waitForUpdate(() => {
-        expect(vm.$el.firstChild.selectedIndex).toBe(1)
-      }).then(done)
-    })
-
-    it('remove v-model', done => {
-      const vm = makeInstance(false)
-
-      expect(vm.$el.firstChild.selectedIndex).toBe(1)
-      vm.foo = true
-      waitForUpdate(() => {
-        expect(vm.$el.firstChild.selectedIndex).toBe(0)
-      }).then(done)
-    })
-  })
-
-  // #7928
-  it('should correctly handle option with date value', done => {
-    const vm = new Vue({
-      data: {
-        dates: [
-          new Date(1520000000000),
-          new Date(1522000000000),
-          new Date(1516000000000)
-        ],
-        selectedDate: null
-      },
-      template:
-        '<div>' +
-        '<select v-model="selectedDate">' +
-        '<option v-for="(date, i) in dates" :key="i" :value="date">' +
-        '{{date}}' +
-        '</option>' +
-        '</select>' +
-        '</div>'
-    }).$mount()
-
-    vm.selectedDate = vm.dates[2]
-    waitForUpdate(() => {
-      expect(vm.$el.firstChild.selectedIndex).toBe(2)
-    }).then(done)
-  })
-})
diff --git a/test/unit/features/directives/model-text.spec.ts b/test/unit/features/directives/model-text.spec.ts
deleted file mode 100644
index 467fd3c585c..00000000000
--- a/test/unit/features/directives/model-text.spec.ts
+++ /dev/null
@@ -1,488 +0,0 @@
-import Vue from 'vue'
-import { isIE9, isIE, isAndroid } from 'core/util/env'
-
-describe('Directive v-model text', () => {
-  it('should update value both ways', done => {
-    const vm = new Vue({
-      data: {
-        test: 'b'
-      },
-      template: '<input v-model="test">'
-    }).$mount()
-    expect(vm.$el.value).toBe('b')
-    vm.test = 'a'
-    waitForUpdate(() => {
-      expect(vm.$el.value).toBe('a')
-      vm.$el.value = 'c'
-      triggerEvent(vm.$el, 'input')
-      expect(vm.test).toBe('c')
-    }).then(done)
-  })
-
-  it('should work with space ended expression in v-model', () => {
-    const vm = new Vue({
-      data: {
-        obj: {
-          test: 'b'
-        }
-      },
-      template: '<input v-model="obj.test ">'
-    }).$mount()
-
-    triggerEvent(vm.$el, 'input')
-    expect(vm.obj['test ']).toBe(undefined)
-    expect(vm.obj.test).toBe('b')
-  })
-
-  it('.lazy modifier', () => {
-    const vm = new Vue({
-      data: {
-        test: 'b'
-      },
-      template: '<input v-model.lazy="test">'
-    }).$mount()
-    expect(vm.$el.value).toBe('b')
-    expect(vm.test).toBe('b')
-    vm.$el.value = 'c'
-    triggerEvent(vm.$el, 'input')
-    expect(vm.test).toBe('b')
-    triggerEvent(vm.$el, 'change')
-    expect(vm.test).toBe('c')
-  })
-
-  it('.number modifier', () => {
-    const vm = new Vue({
-      data: {
-        test: 1
-      },
-      template: '<input v-model.number="test">'
-    }).$mount()
-    expect(vm.test).toBe(1)
-    vm.$el.value = '2'
-    triggerEvent(vm.$el, 'input')
-    expect(vm.test).toBe(2)
-    // should let strings pass through
-    vm.$el.value = 'f'
-    triggerEvent(vm.$el, 'input')
-    expect(vm.test).toBe('f')
-  })
-
-  it('.trim modifier', () => {
-    const vm = new Vue({
-      data: {
-        test: 'hi'
-      },
-      template: '<input v-model.trim="test">'
-    }).$mount()
-    expect(vm.test).toBe('hi')
-    vm.$el.value = ' what '
-    triggerEvent(vm.$el, 'input')
-    expect(vm.test).toBe('what')
-  })
-
-  it('.number focus and typing', done => {
-    const vm = new Vue({
-      data: {
-        test: 0,
-        update: 0
-      },
-      template:
-        '<div>' +
-        '<input ref="input" v-model.number="test">{{ update }}' +
-        '<input ref="blur">' +
-        '</div>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    vm.$refs.input.focus()
-    expect(vm.test).toBe(0)
-    vm.$refs.input.value = '1.0'
-    triggerEvent(vm.$refs.input, 'input')
-    expect(vm.test).toBe(1)
-    vm.update++
-    waitForUpdate(() => {
-      expect(vm.$refs.input.value).toBe('1.0')
-      vm.$refs.blur.focus()
-      vm.update++
-    })
-      .then(() => {
-        expect(vm.$refs.input.value).toBe('1')
-      })
-      .then(done)
-  })
-
-  it('.trim focus and typing', done => {
-    const vm = new Vue({
-      data: {
-        test: 'abc',
-        update: 0
-      },
-      template:
-        '<div>' +
-        '<input ref="input" v-model.trim="test" type="text">{{ update }}' +
-        '<input ref="blur"/>' +
-        '</div>'
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    vm.$refs.input.focus()
-    vm.$refs.input.value = ' abc '
-    triggerEvent(vm.$refs.input, 'input')
-    expect(vm.test).toBe('abc')
-    vm.update++
-    waitForUpdate(() => {
-      expect(vm.$refs.input.value).toBe(' abc ')
-      vm.$refs.blur.focus()
-      vm.update++
-    })
-      .then(() => {
-        expect(vm.$refs.input.value).toBe('abc')
-      })
-      .then(done)
-  })
-
-  it('multiple inputs', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        selections: [
-          [1, 2, 3],
-          [4, 5]
-        ],
-        inputList: [
-          {
-            name: 'questionA',
-            data: ['a', 'b', 'c']
-          },
-          {
-            name: 'questionB',
-            data: ['1', '2']
-          }
-        ]
-      },
-      watch: {
-        selections: spy
-      },
-      template:
-        '<div>' +
-        '<div v-for="(inputGroup, idx) in inputList">' +
-        '<div>' +
-        '<span v-for="(item, index) in inputGroup.data">' +
-        '<input v-bind:name="item" type="text" v-model.number="selections[idx][index]" v-bind:id="idx+\'-\'+index"/>' +
-        '<label>{{item}}</label>' +
-        '</span>' +
-        '</div>' +
-        '</div>' +
-        '<span ref="rs">{{selections}}</span>' +
-        '</div>'
-    }).$mount()
-    const inputs = vm.$el.getElementsByTagName('input')
-    inputs[1].value = 'test'
-    triggerEvent(inputs[1], 'input')
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalled()
-      expect(vm.selections).toEqual([
-        [1, 'test', 3],
-        [4, 5]
-      ])
-    }).then(done)
-  })
-
-  if (isIE9) {
-    it('IE9 selectionchange', done => {
-      const vm = new Vue({
-        data: {
-          test: 'foo'
-        },
-        template: '<input v-model="test">'
-      }).$mount()
-      const input = vm.$el
-      input.value = 'bar'
-      document.body.appendChild(input)
-      input.focus()
-      triggerEvent(input, 'selectionchange')
-      waitForUpdate(() => {
-        expect(vm.test).toBe('bar')
-        input.value = 'a'
-        triggerEvent(input, 'selectionchange')
-        expect(vm.test).toBe('a')
-      }).then(done)
-    })
-  }
-
-  it('compositionevents', function (done) {
-    const vm = new Vue({
-      data: {
-        test: 'foo'
-      },
-      template: '<input v-model="test">'
-    }).$mount()
-    const input = vm.$el
-    triggerEvent(input, 'compositionstart')
-    input.value = 'baz'
-    // input before composition unlock should not call set
-    triggerEvent(input, 'input')
-    expect(vm.test).toBe('foo')
-    // after composition unlock it should work
-    triggerEvent(input, 'compositionend')
-    triggerEvent(input, 'input')
-    expect(vm.test).toBe('baz')
-    done()
-  })
-
-  it('warn invalid tag', () => {
-    new Vue({
-      data: {
-        test: 'foo'
-      },
-      template: '<div v-model="test"></div>'
-    }).$mount()
-    expect(
-      '<div v-model="test">: v-model is not supported on this element type'
-    ).toHaveBeenWarned()
-  })
-
-  // #3468
-  it('should have higher priority than user v-on events', () => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        a: 'a'
-      },
-      template: '<input v-model="a" @input="onInput">',
-      methods: {
-        onInput(e) {
-          spy(this.a)
-        }
-      }
-    }).$mount()
-    vm.$el.value = 'b'
-    triggerEvent(vm.$el, 'input')
-    expect(spy).toHaveBeenCalledWith('b')
-  })
-
-  it('warn binding to v-for alias', () => {
-    new Vue({
-      data: {
-        strings: ['hi']
-      },
-      template: `
-        <div>
-          <div v-for="str in strings">
-            <input v-model="str">
-          </div>
-        </div>
-      `
-    }).$mount()
-    expect(
-      'You are binding v-model directly to a v-for iteration alias'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn if v-model and v-bind:value conflict', () => {
-    new Vue({
-      data: {
-        test: 'foo'
-      },
-      template: '<input type="text" v-model="test" v-bind:value="test">'
-    }).$mount()
-    expect('v-bind:value="test" conflicts with v-model').toHaveBeenWarned()
-  })
-
-  it('warn if v-model and :value conflict', () => {
-    new Vue({
-      data: {
-        test: 'foo'
-      },
-      template: '<input type="text" v-model="test" :value="test">'
-    }).$mount()
-    expect(':value="test" conflicts with v-model').toHaveBeenWarned()
-  })
-
-  it('should not warn on radio, checkbox, or custom component', () => {
-    new Vue({
-      data: { test: '' },
-      components: {
-        foo: {
-          props: ['model', 'value'],
-          model: { prop: 'model', event: 'change' },
-          template: `<div/>`
-        }
-      },
-      template: `
-        <div>
-          <input type="checkbox" v-model="test" :value="test">
-          <input type="radio" v-model="test" :value="test">
-          <foo v-model="test" :value="test"/>
-        </div>
-      `
-    }).$mount()
-    expect('conflicts with v-model').not.toHaveBeenWarned()
-  })
-
-  it('should not warn on input with dynamic type binding', () => {
-    new Vue({
-      data: {
-        type: 'checkbox',
-        test: 'foo'
-      },
-      template: '<input :type="type" v-model="test" :value="test">'
-    }).$mount()
-    expect('conflicts with v-model').not.toHaveBeenWarned()
-  })
-
-  if (!isAndroid) {
-    it('does not trigger extra input events with single compositionend', () => {
-      const spy = vi.fn()
-      const vm = new Vue({
-        data: {
-          a: 'a'
-        },
-        template: '<input v-model="a" @input="onInput">',
-        methods: {
-          onInput(e) {
-            spy(e.target.value)
-          }
-        }
-      }).$mount()
-      expect(spy.mock.calls.length).toBe(0)
-      vm.$el.value = 'b'
-      triggerEvent(vm.$el, 'input')
-      expect(spy.mock.calls.length).toBe(1)
-      triggerEvent(vm.$el, 'compositionend')
-      expect(spy.mock.calls.length).toBe(1)
-    })
-
-    it('triggers extra input on compositionstart + end', () => {
-      const spy = vi.fn()
-      const vm = new Vue({
-        data: {
-          a: 'a'
-        },
-        template: '<input v-model="a" @input="onInput">',
-        methods: {
-          onInput(e) {
-            spy(e.target.value)
-          }
-        }
-      }).$mount()
-      expect(spy.mock.calls.length).toBe(0)
-      vm.$el.value = 'b'
-      triggerEvent(vm.$el, 'input')
-      expect(spy.mock.calls.length).toBe(1)
-      triggerEvent(vm.$el, 'compositionstart')
-      triggerEvent(vm.$el, 'compositionend')
-      expect(spy.mock.calls.length).toBe(2)
-    })
-
-    // #4392
-    it('should not update value with modifiers when in focus if post-conversion values are the same', done => {
-      const vm = new Vue({
-        data: {
-          a: 1,
-          foo: false
-        },
-        template: '<div>{{ foo }}<input ref="input" v-model.number="a"></div>'
-      }).$mount()
-
-      document.body.appendChild(vm.$el)
-      vm.$refs.input.focus()
-      vm.$refs.input.value = '1.000'
-      vm.foo = true
-
-      waitForUpdate(() => {
-        expect(vm.$refs.input.value).toBe('1.000')
-      }).then(done)
-    })
-
-    // #6552
-    // This was original introduced due to the microtask between DOM events issue
-    // but fixed after switching to MessageChannel.
-    it('should not block input when another input listener with modifier is used', done => {
-      const vm = new Vue({
-        data: {
-          a: 'a',
-          foo: false
-        },
-        template: `
-          <div>
-            <input ref="input" v-model="a" @input.capture="onInput">{{ a }}
-            <div v-if="foo">foo</div>
-          </div>
-        `,
-        methods: {
-          onInput(e) {
-            this.foo = true
-          }
-        }
-      }).$mount()
-
-      document.body.appendChild(vm.$el)
-      vm.$refs.input.focus()
-      vm.$refs.input.value = 'b'
-      triggerEvent(vm.$refs.input, 'input')
-
-      // not using wait for update here because there will be two update cycles
-      // one caused by onInput in the first listener
-      setTimeout(() => {
-        expect(vm.a).toBe('b')
-        expect(vm.$refs.input.value).toBe('b')
-        done()
-      }, 16)
-    })
-
-    it('should create and make reactive non-existent properties', done => {
-      const vm = new Vue({
-        data: {
-          foo: {}
-        },
-        template: '<input v-model="foo.bar">'
-      }).$mount()
-      expect(vm.$el.value).toBe('')
-
-      vm.$el.value = 'a'
-      triggerEvent(vm.$el, 'input')
-      expect(vm.foo.bar).toBe('a')
-      vm.foo.bar = 'b'
-      waitForUpdate(() => {
-        expect(vm.$el.value).toBe('b')
-        vm.foo = {}
-      })
-        .then(() => {
-          expect(vm.$el.value).toBe('')
-        })
-        .then(done)
-    })
-  }
-
-  if (isIE && !isIE9) {
-    // #7138
-    it('should not fire input on initial render of textarea with placeholder in IE10/11', done => {
-      const el = document.createElement('div')
-      document.body.appendChild(el)
-      const vm = new Vue({
-        el,
-        data: { foo: null },
-        template: `<textarea v-model="foo" placeholder="bar"></textarea>`
-      })
-      setTimeout(() => {
-        expect(vm.foo).toBe(null)
-        done()
-      }, 17)
-    })
-
-    // #9042
-    it('should not block the first input event when placeholder is empty', done => {
-      const el = document.createElement('div')
-      document.body.appendChild(el)
-      const vm = new Vue({
-        el,
-        data: { evtCount: 0 },
-        template: `<textarea placeholder="" @input="evtCount++"></textarea>`
-      })
-      triggerEvent(vm.$el, 'input')
-      setTimeout(() => {
-        expect(vm.evtCount).toBe(1)
-        done()
-      }, 17)
-    })
-  }
-})
diff --git a/test/unit/features/directives/on.spec.ts b/test/unit/features/directives/on.spec.ts
deleted file mode 100644
index e103301471e..00000000000
--- a/test/unit/features/directives/on.spec.ts
+++ /dev/null
@@ -1,1211 +0,0 @@
-import Vue from 'vue'
-import { supportsPassive } from 'core/util/env'
-import { SpyInstanceFn } from 'vitest'
-
-describe('Directive v-on', () => {
-  let vm, spy: SpyInstanceFn, el: HTMLElement
-
-  beforeEach(() => {
-    vm = null
-    spy = vi.fn()
-    el = document.createElement('div')
-    document.body.appendChild(el)
-  })
-
-  afterEach(() => {
-    if (vm) {
-      document.body.removeChild(vm.$el)
-    }
-  })
-
-  it('should bind event to a method', () => {
-    vm = new Vue({
-      el,
-      template: '<div v-on:click="foo"></div>',
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-
-    const args = spy.mock.calls
-    const event = (args[0] && args[0][0]) || {}
-    expect(event.type).toBe('click')
-  })
-
-  it('should bind event to an inline statement', () => {
-    vm = new Vue({
-      el,
-      template: '<div v-on:click="foo(1,2,3,$event)"></div>',
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-
-    const args = spy.mock.calls
-    const firstArgs = args[0]
-    expect(firstArgs.length).toBe(4)
-    expect(firstArgs[0]).toBe(1)
-    expect(firstArgs[1]).toBe(2)
-    expect(firstArgs[2]).toBe(3)
-    expect(firstArgs[3].type).toBe('click')
-  })
-
-  it('should support inline function expression', () => {
-    const spy = vi.fn()
-    vm = new Vue({
-      el,
-      template: `<div class="test" @click="function (e) { log(e.target.className) }"></div>`,
-      methods: {
-        log: spy
-      }
-    }).$mount()
-    triggerEvent(vm.$el, 'click')
-    expect(spy).toHaveBeenCalledWith('test')
-  })
-
-  it('should support shorthand', () => {
-    vm = new Vue({
-      el,
-      template: '<a href="#test" @click.prevent="foo"></a>',
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-  })
-
-  it('should support stop propagation', () => {
-    vm = new Vue({
-      el,
-      template: `
-        <div @click.stop="foo"></div>
-      `,
-      methods: { foo: spy }
-    })
-    const hash = window.location.hash
-    triggerEvent(vm.$el, 'click')
-    expect(window.location.hash).toBe(hash)
-  })
-
-  it('should support prevent default', () => {
-    vm = new Vue({
-      el,
-      template: `
-        <input type="checkbox" ref="input" @click.prevent="foo">
-      `,
-      methods: {
-        foo($event) {
-          spy($event.defaultPrevented)
-        }
-      }
-    })
-    vm.$refs.input.checked = false
-    triggerEvent(vm.$refs.input, 'click')
-    expect(spy).toHaveBeenCalledWith(true)
-  })
-
-  it('should support capture', () => {
-    const callOrder: any[] = []
-    vm = new Vue({
-      el,
-      template: `
-        <div @click.capture="foo">
-          <div @click="bar"></div>
-        </div>
-      `,
-      methods: {
-        foo() {
-          callOrder.push(1)
-        },
-        bar() {
-          callOrder.push(2)
-        }
-      }
-    })
-    triggerEvent(vm.$el.firstChild, 'click')
-    expect(callOrder.toString()).toBe('1,2')
-  })
-
-  it('should support once', () => {
-    vm = new Vue({
-      el,
-      template: `
-        <div @click.once="foo">
-        </div>
-      `,
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-    triggerEvent(vm.$el, 'click')
-    expect(spy.mock.calls.length).toBe(1) // should no longer trigger
-  })
-
-  // #4655
-  it('should handle .once on multiple elements properly', () => {
-    vm = new Vue({
-      el,
-      template: `
-        <div>
-          <button ref="one" @click.once="foo">one</button>
-          <button ref="two" @click.once="foo">two</button>
-        </div>
-      `,
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$refs.one, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-    triggerEvent(vm.$refs.one, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-    triggerEvent(vm.$refs.two, 'click')
-    expect(spy.mock.calls.length).toBe(2)
-    triggerEvent(vm.$refs.one, 'click')
-    triggerEvent(vm.$refs.two, 'click')
-    expect(spy.mock.calls.length).toBe(2)
-  })
-
-  it('should support capture and once', () => {
-    const callOrder: any[] = []
-    vm = new Vue({
-      el,
-      template: `
-        <div @click.capture.once="foo">
-          <div @click="bar"></div>
-        </div>
-      `,
-      methods: {
-        foo() {
-          callOrder.push(1)
-        },
-        bar() {
-          callOrder.push(2)
-        }
-      }
-    })
-    triggerEvent(vm.$el.firstChild, 'click')
-    expect(callOrder.toString()).toBe('1,2')
-    triggerEvent(vm.$el.firstChild, 'click')
-    expect(callOrder.toString()).toBe('1,2,2')
-  })
-
-  // #4846
-  it('should support once and other modifiers', () => {
-    vm = new Vue({
-      el,
-      template: `<div @click.once.self="foo"><span/></div>`,
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el.firstChild, 'click')
-    expect(spy).not.toHaveBeenCalled()
-    triggerEvent(vm.$el, 'click')
-    expect(spy).toHaveBeenCalled()
-    triggerEvent(vm.$el, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-  })
-
-  it('should support keyCode', () => {
-    vm = new Vue({
-      el,
-      template: `<input @keyup.enter="foo">`,
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el, 'keyup', e => {
-      e.keyCode = 13
-    })
-    expect(spy).toHaveBeenCalled()
-  })
-
-  it('should support automatic key name inference', () => {
-    vm = new Vue({
-      el,
-      template: `<input @keyup.arrow-right="foo">`,
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el, 'keyup', e => {
-      e.key = 'ArrowRight'
-    })
-    expect(spy).toHaveBeenCalled()
-  })
-
-  // ctrl, shift, alt, meta
-  it('should support system modifiers', () => {
-    vm = new Vue({
-      el,
-      template: `
-        <div>
-          <input ref="ctrl" @keyup.ctrl="foo">
-          <input ref="shift" @keyup.shift="foo">
-          <input ref="alt" @keyup.alt="foo">
-          <input ref="meta" @keyup.meta="foo">
-        </div>
-      `,
-      methods: { foo: spy }
-    })
-
-    triggerEvent(vm.$refs.ctrl, 'keyup')
-    expect(spy.mock.calls.length).toBe(0)
-    triggerEvent(vm.$refs.ctrl, 'keyup', e => {
-      e.ctrlKey = true
-    })
-    expect(spy.mock.calls.length).toBe(1)
-
-    triggerEvent(vm.$refs.shift, 'keyup')
-    expect(spy.mock.calls.length).toBe(1)
-    triggerEvent(vm.$refs.shift, 'keyup', e => {
-      e.shiftKey = true
-    })
-    expect(spy.mock.calls.length).toBe(2)
-
-    triggerEvent(vm.$refs.alt, 'keyup')
-    expect(spy.mock.calls.length).toBe(2)
-    triggerEvent(vm.$refs.alt, 'keyup', e => {
-      e.altKey = true
-    })
-    expect(spy.mock.calls.length).toBe(3)
-
-    triggerEvent(vm.$refs.meta, 'keyup')
-    expect(spy.mock.calls.length).toBe(3)
-    triggerEvent(vm.$refs.meta, 'keyup', e => {
-      e.metaKey = true
-    })
-    expect(spy.mock.calls.length).toBe(4)
-  })
-
-  it('should support exact modifier', () => {
-    vm = new Vue({
-      el,
-      template: `
-        <div>
-          <input ref="ctrl" @keyup.exact="foo">
-        </div>
-      `,
-      methods: { foo: spy }
-    })
-
-    triggerEvent(vm.$refs.ctrl, 'keyup')
-    expect(spy.mock.calls.length).toBe(1)
-
-    triggerEvent(vm.$refs.ctrl, 'keyup', e => {
-      e.ctrlKey = true
-    })
-    expect(spy.mock.calls.length).toBe(1)
-
-    // should not trigger if has other system modifiers
-    triggerEvent(vm.$refs.ctrl, 'keyup', e => {
-      e.ctrlKey = true
-      e.altKey = true
-    })
-    expect(spy.mock.calls.length).toBe(1)
-  })
-
-  it('should support system modifiers with exact', () => {
-    vm = new Vue({
-      el,
-      template: `
-        <div>
-          <input ref="ctrl" @keyup.ctrl.exact="foo">
-        </div>
-      `,
-      methods: { foo: spy }
-    })
-
-    triggerEvent(vm.$refs.ctrl, 'keyup')
-    expect(spy.mock.calls.length).toBe(0)
-
-    triggerEvent(vm.$refs.ctrl, 'keyup', e => {
-      e.ctrlKey = true
-    })
-    expect(spy.mock.calls.length).toBe(1)
-
-    // should not trigger if has other system modifiers
-    triggerEvent(vm.$refs.ctrl, 'keyup', e => {
-      e.ctrlKey = true
-      e.altKey = true
-    })
-    expect(spy.mock.calls.length).toBe(1)
-  })
-
-  it('should support number keyCode', () => {
-    vm = new Vue({
-      el,
-      template: `<input @keyup.13="foo">`,
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el, 'keyup', e => {
-      e.keyCode = 13
-    })
-    expect(spy).toHaveBeenCalled()
-  })
-
-  it('should support mouse modifier', () => {
-    const left = 0
-    const middle = 1
-    const right = 2
-    const spyLeft = vi.fn()
-    const spyMiddle = vi.fn()
-    const spyRight = vi.fn()
-
-    vm = new Vue({
-      el,
-      template: `
-        <div>
-          <div ref="left" @mousedown.left="foo">left</div>
-          <div ref="right" @mousedown.right="foo1">right</div>
-          <div ref="middle" @mousedown.middle="foo2">right</div>
-        </div>
-      `,
-      methods: {
-        foo: spyLeft,
-        foo1: spyRight,
-        foo2: spyMiddle
-      }
-    })
-
-    triggerEvent(vm.$refs.left, 'mousedown', e => {
-      e.button = right
-    })
-    triggerEvent(vm.$refs.left, 'mousedown', e => {
-      e.button = middle
-    })
-    expect(spyLeft).not.toHaveBeenCalled()
-    triggerEvent(vm.$refs.left, 'mousedown', e => {
-      e.button = left
-    })
-    expect(spyLeft).toHaveBeenCalled()
-
-    triggerEvent(vm.$refs.right, 'mousedown', e => {
-      e.button = left
-    })
-    triggerEvent(vm.$refs.right, 'mousedown', e => {
-      e.button = middle
-    })
-    expect(spyRight).not.toHaveBeenCalled()
-    triggerEvent(vm.$refs.right, 'mousedown', e => {
-      e.button = right
-    })
-    expect(spyRight).toHaveBeenCalled()
-
-    triggerEvent(vm.$refs.middle, 'mousedown', e => {
-      e.button = left
-    })
-    triggerEvent(vm.$refs.middle, 'mousedown', e => {
-      e.button = right
-    })
-    expect(spyMiddle).not.toHaveBeenCalled()
-    triggerEvent(vm.$refs.middle, 'mousedown', e => {
-      e.button = middle
-    })
-    expect(spyMiddle).toHaveBeenCalled()
-  })
-
-  it('should support KeyboardEvent.key for built in aliases', () => {
-    vm = new Vue({
-      el,
-      template: `
-        <div>
-          <input ref="enter" @keyup.enter="foo">
-          <input ref="space" @keyup.space="foo">
-          <input ref="esc" @keyup.esc="foo">
-          <input ref="left" @keyup.left="foo">
-          <input ref="delete" @keyup.delete="foo">
-        </div>
-      `,
-      methods: { foo: spy }
-    })
-
-    triggerEvent(vm.$refs.enter, 'keyup', e => {
-      e.key = 'Enter'
-    })
-    expect(spy.mock.calls.length).toBe(1)
-    triggerEvent(vm.$refs.space, 'keyup', e => {
-      e.key = ' '
-    })
-    expect(spy.mock.calls.length).toBe(2)
-    triggerEvent(vm.$refs.esc, 'keyup', e => {
-      e.key = 'Escape'
-    })
-    expect(spy.mock.calls.length).toBe(3)
-    triggerEvent(vm.$refs.left, 'keyup', e => {
-      e.key = 'ArrowLeft'
-    })
-    expect(spy.mock.calls.length).toBe(4)
-    triggerEvent(vm.$refs.delete, 'keyup', e => {
-      e.key = 'Backspace'
-    })
-    expect(spy.mock.calls.length).toBe(5)
-    triggerEvent(vm.$refs.delete, 'keyup', e => {
-      e.key = 'Delete'
-    })
-    expect(spy.mock.calls.length).toBe(6)
-  })
-
-  it('should support custom keyCode', () => {
-    Vue.config.keyCodes.test = 1
-    vm = new Vue({
-      el,
-      template: `<input @keyup.test="foo">`,
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el, 'keyup', e => {
-      e.keyCode = 1
-    })
-    expect(spy).toHaveBeenCalled()
-    Vue.config.keyCodes = Object.create(null)
-  })
-
-  it('should override built-in keyCode', () => {
-    Vue.config.keyCodes.up = [1, 87]
-    vm = new Vue({
-      el,
-      template: `<input @keyup.up="foo" @keyup.down="foo">`,
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el, 'keyup', e => {
-      e.keyCode = 87
-    })
-    expect(spy).toHaveBeenCalled()
-    triggerEvent(vm.$el, 'keyup', e => {
-      e.keyCode = 1
-    })
-    expect(spy).toHaveBeenCalledTimes(2)
-    // should not affect built-in down keycode
-    triggerEvent(vm.$el, 'keyup', e => {
-      e.keyCode = 40
-    })
-    expect(spy).toHaveBeenCalledTimes(3)
-    Vue.config.keyCodes = Object.create(null)
-  })
-
-  it('should bind to a child component', () => {
-    vm = new Vue({
-      el,
-      template: '<bar @custom="foo"></bar>',
-      methods: { foo: spy },
-      components: {
-        bar: {
-          template: '<span>Hello</span>'
-        }
-      }
-    })
-    vm.$children[0].$emit('custom', 'foo', 'bar')
-    expect(spy).toHaveBeenCalledWith('foo', 'bar')
-  })
-
-  it('should be able to bind native events for a child component', () => {
-    vm = new Vue({
-      el,
-      template: '<bar @click.native="foo"></bar>',
-      methods: { foo: spy },
-      components: {
-        bar: {
-          template: '<span>Hello</span>'
-        }
-      }
-    })
-    vm.$children[0].$emit('click')
-    expect(spy).not.toHaveBeenCalled()
-    triggerEvent(vm.$children[0].$el, 'click')
-    expect(spy).toHaveBeenCalled()
-  })
-
-  it('should throw a warning if native modifier is used on native HTML element', () => {
-    vm = new Vue({
-      el,
-      template: `
-        <button @click.native="foo"></button>
-      `,
-      methods: { foo: spy }
-    })
-
-    triggerEvent(vm.$el, 'click')
-    expect(
-      `The .native modifier for v-on is only valid on components but it was used on <button>.`
-    ).toHaveBeenWarned()
-    expect(spy.mock.calls.length).toBe(0)
-  })
-
-  it('should not throw a warning if native modifier is used on a dynamic component', () => {
-    vm = new Vue({
-      el,
-      template: `
-        <component is="div" @click.native="foo('native')" @click="foo('regular')"/>
-      `,
-      methods: { foo: spy }
-    })
-
-    triggerEvent(vm.$el, 'click')
-    expect(
-      `The .native modifier for v-on is only valid on components but it was used on <div>.`
-    ).not.toHaveBeenWarned()
-    expect(spy.mock.calls).toEqual([['regular']]) // Regular @click should work for dynamic components resolved to native HTML elements.
-  })
-
-  it('.once modifier should work with child components', () => {
-    vm = new Vue({
-      el,
-      template: '<bar @custom.once="foo"></bar>',
-      methods: { foo: spy },
-      components: {
-        bar: {
-          template: '<span>Hello</span>'
-        }
-      }
-    })
-    vm.$children[0].$emit('custom')
-    expect(spy.mock.calls.length).toBe(1)
-    vm.$children[0].$emit('custom')
-    expect(spy.mock.calls.length).toBe(1) // should not be called again
-  })
-
-  it('remove listener', done => {
-    const spy2 = vi.fn()
-    vm = new Vue({
-      el,
-      methods: { foo: spy, bar: spy2 },
-      data: {
-        ok: true
-      },
-      render(h) {
-        return this.ok
-          ? h('input', { on: { click: this.foo } })
-          : h('input', { on: { input: this.bar } })
-      }
-    })
-    triggerEvent(vm.$el, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-    expect(spy2.mock.calls.length).toBe(0)
-    vm.ok = false
-    waitForUpdate(() => {
-      triggerEvent(vm.$el, 'click')
-      expect(spy.mock.calls.length).toBe(1) // should no longer trigger
-      triggerEvent(vm.$el, 'input')
-      expect(spy2.mock.calls.length).toBe(1)
-    }).then(done)
-  })
-
-  it('remove capturing listener', done => {
-    const spy2 = vi.fn()
-    vm = new Vue({
-      el,
-      methods: {
-        foo: spy,
-        bar: spy2,
-        stopped(ev) {
-          ev.stopPropagation()
-        }
-      },
-      data: {
-        ok: true
-      },
-      render(h) {
-        return this.ok
-          ? h('div', { on: { '!click': this.foo } }, [
-              h('div', { on: { click: this.stopped } })
-            ])
-          : h('div', { on: { mouseOver: this.bar } }, [h('div')])
-      }
-    })
-    triggerEvent(vm.$el.firstChild, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-    expect(spy2.mock.calls.length).toBe(0)
-    vm.ok = false
-    waitForUpdate(() => {
-      triggerEvent(vm.$el.firstChild, 'click')
-      expect(spy.mock.calls.length).toBe(1) // should no longer trigger
-      triggerEvent(vm.$el, 'mouseOver')
-      expect(spy2.mock.calls.length).toBe(1)
-    }).then(done)
-  })
-
-  it('remove once listener', done => {
-    const spy2 = vi.fn()
-    vm = new Vue({
-      el,
-      methods: { foo: spy, bar: spy2 },
-      data: {
-        ok: true
-      },
-      render(h) {
-        return this.ok
-          ? h('input', { on: { '~click': this.foo } })
-          : h('input', { on: { input: this.bar } })
-      }
-    })
-    triggerEvent(vm.$el, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-    triggerEvent(vm.$el, 'click')
-    expect(spy.mock.calls.length).toBe(1) // should no longer trigger
-    expect(spy2.mock.calls.length).toBe(0)
-    vm.ok = false
-    waitForUpdate(() => {
-      triggerEvent(vm.$el, 'click')
-      expect(spy.mock.calls.length).toBe(1) // should no longer trigger
-      triggerEvent(vm.$el, 'input')
-      expect(spy2.mock.calls.length).toBe(1)
-    }).then(done)
-  })
-
-  it('remove capturing and once listener', done => {
-    const spy2 = vi.fn()
-    vm = new Vue({
-      el,
-      methods: {
-        foo: spy,
-        bar: spy2,
-        stopped(ev) {
-          ev.stopPropagation()
-        }
-      },
-      data: {
-        ok: true
-      },
-      render(h) {
-        return this.ok
-          ? h('div', { on: { '~!click': this.foo } }, [
-              h('div', { on: { click: this.stopped } })
-            ])
-          : h('div', { on: { mouseOver: this.bar } }, [h('div')])
-      }
-    })
-    triggerEvent(vm.$el.firstChild, 'click')
-    expect(spy.mock.calls.length).toBe(1)
-    triggerEvent(vm.$el.firstChild, 'click')
-    expect(spy.mock.calls.length).toBe(1) // should no longer trigger
-    expect(spy2.mock.calls.length).toBe(0)
-    vm.ok = false
-    waitForUpdate(() => {
-      triggerEvent(vm.$el.firstChild, 'click')
-      expect(spy.mock.calls.length).toBe(1) // should no longer trigger
-      triggerEvent(vm.$el, 'mouseOver')
-      expect(spy2.mock.calls.length).toBe(1)
-    }).then(done)
-  })
-
-  it('remove listener on child component', done => {
-    const spy2 = vi.fn()
-    vm = new Vue({
-      el,
-      methods: { foo: spy, bar: spy2 },
-      data: {
-        ok: true
-      },
-      components: {
-        test: {
-          template: '<div></div>'
-        }
-      },
-      render(h) {
-        return this.ok
-          ? h('test', { on: { foo: this.foo } })
-          : h('test', { on: { bar: this.bar } })
-      }
-    })
-    vm.$children[0].$emit('foo')
-    expect(spy.mock.calls.length).toBe(1)
-    expect(spy2.mock.calls.length).toBe(0)
-    vm.ok = false
-    waitForUpdate(() => {
-      vm.$children[0].$emit('foo')
-      expect(spy.mock.calls.length).toBe(1) // should no longer trigger
-      vm.$children[0].$emit('bar')
-      expect(spy2.mock.calls.length).toBe(1)
-    }).then(done)
-  })
-
-  it('warn missing handlers', () => {
-    vm = new Vue({
-      el,
-      data: { none: null },
-      template: `<div @click="none"></div>`
-    })
-    expect(`Invalid handler for event "click": got null`).toHaveBeenWarned()
-    expect(() => {
-      triggerEvent(vm.$el, 'click')
-    }).not.toThrow()
-  })
-
-  // Github Issue #5046
-  it('should support keyboard modifier for direction keys', () => {
-    const spyLeft = vi.fn()
-    const spyRight = vi.fn()
-    const spyUp = vi.fn()
-    const spyDown = vi.fn()
-    vm = new Vue({
-      el,
-      template: `
-        <div>
-          <input ref="left" @keydown.left="foo"></input>
-          <input ref="right" @keydown.right="foo1"></input>
-          <input ref="up" @keydown.up="foo2"></input>
-          <input ref="down" @keydown.down="foo3"></input>
-        </div>
-      `,
-      methods: {
-        foo: spyLeft,
-        foo1: spyRight,
-        foo2: spyUp,
-        foo3: spyDown
-      }
-    })
-    triggerEvent(vm.$refs.left, 'keydown', e => {
-      e.keyCode = 37
-    })
-    triggerEvent(vm.$refs.left, 'keydown', e => {
-      e.keyCode = 39
-    })
-
-    triggerEvent(vm.$refs.right, 'keydown', e => {
-      e.keyCode = 39
-    })
-    triggerEvent(vm.$refs.right, 'keydown', e => {
-      e.keyCode = 38
-    })
-
-    triggerEvent(vm.$refs.up, 'keydown', e => {
-      e.keyCode = 38
-    })
-    triggerEvent(vm.$refs.up, 'keydown', e => {
-      e.keyCode = 37
-    })
-
-    triggerEvent(vm.$refs.down, 'keydown', e => {
-      e.keyCode = 40
-    })
-    triggerEvent(vm.$refs.down, 'keydown', e => {
-      e.keyCode = 39
-    })
-
-    expect(spyLeft.mock.calls.length).toBe(1)
-    expect(spyRight.mock.calls.length).toBe(1)
-    expect(spyUp.mock.calls.length).toBe(1)
-    expect(spyDown.mock.calls.length).toBe(1)
-  })
-
-  // This test case should only run when the test browser supports passive.
-  if (supportsPassive) {
-    it('should support passive', () => {
-      vm = new Vue({
-        el,
-        template: `
-          <div>
-            <input type="checkbox" ref="normal" @click="foo"/>
-            <input type="checkbox" ref="passive" @click.passive="foo"/>
-            <input type="checkbox" ref="exclusive" @click.prevent.passive/>
-          </div>
-        `,
-        methods: {
-          foo(e) {
-            e.preventDefault()
-          }
-        }
-      })
-
-      vm.$refs.normal.checked = false
-      vm.$refs.passive.checked = false
-      vm.$refs.exclusive.checked = false
-      vm.$refs.normal.click()
-      vm.$refs.passive.click()
-      vm.$refs.exclusive.click()
-      expect(vm.$refs.normal.checked).toBe(false)
-      expect(vm.$refs.passive.checked).toBe(true)
-      expect(vm.$refs.exclusive.checked).toBe(true)
-      expect(
-        "passive and prevent can't be used together. Passive handler can't prevent default event."
-      ).toHaveBeenWarned()
-    })
-  }
-
-  // GitHub Issues #5146
-  it('should only prevent when match keycode', () => {
-    let prevented = false
-    vm = new Vue({
-      el,
-      template: `
-        <input ref="input" @keydown.enter.prevent="foo">
-      `,
-      methods: {
-        foo($event) {
-          prevented = $event.defaultPrevented
-        }
-      }
-    })
-
-    triggerEvent(vm.$refs.input, 'keydown', e => {
-      e.keyCode = 32
-    })
-    expect(prevented).toBe(false)
-    triggerEvent(vm.$refs.input, 'keydown', e => {
-      e.keyCode = 13
-    })
-    expect(prevented).toBe(true)
-  })
-
-  it('should transform click.right to contextmenu', () => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      template: `<div @click.right="foo"></div>`,
-      methods: { foo: spy }
-    }).$mount()
-
-    triggerEvent(vm.$el, 'contextmenu')
-    expect(spy).toHaveBeenCalled()
-  })
-
-  it('should transform click.middle to mouseup', () => {
-    const spy = vi.fn()
-    vm = new Vue({
-      el,
-      template: `<div @click.middle="foo"></div>`,
-      methods: { foo: spy }
-    })
-    triggerEvent(vm.$el, 'mouseup', e => {
-      e.button = 0
-    })
-    expect(spy).not.toHaveBeenCalled()
-    triggerEvent(vm.$el, 'mouseup', e => {
-      e.button = 1
-    })
-    expect(spy).toHaveBeenCalled()
-  })
-
-  it('object syntax (no argument)', () => {
-    const click = vi.fn()
-    const mouseup = vi.fn()
-    vm = new Vue({
-      el,
-      template: `<button v-on="listeners">foo</button>`,
-      created() {
-        this.listeners = {
-          click,
-          mouseup
-        }
-      }
-    })
-
-    triggerEvent(vm.$el, 'click')
-    expect(click.mock.calls.length).toBe(1)
-    expect(mouseup.mock.calls.length).toBe(0)
-
-    triggerEvent(vm.$el, 'mouseup')
-    expect(click.mock.calls.length).toBe(1)
-    expect(mouseup.mock.calls.length).toBe(1)
-  })
-
-  it('object syntax (no argument, mixed with normal listeners)', () => {
-    const click1 = vi.fn()
-    const click2 = vi.fn()
-    const mouseup = vi.fn()
-    vm = new Vue({
-      el,
-      template: `<button v-on="listeners" @click="click2">foo</button>`,
-      created() {
-        this.listeners = {
-          click: click1,
-          mouseup
-        }
-      },
-      methods: {
-        click2
-      }
-    })
-
-    triggerEvent(vm.$el, 'click')
-    expect(click1.mock.calls.length).toBe(1)
-    expect(click2.mock.calls.length).toBe(1)
-    expect(mouseup.mock.calls.length).toBe(0)
-
-    triggerEvent(vm.$el, 'mouseup')
-    expect(click1.mock.calls.length).toBe(1)
-    expect(click2.mock.calls.length).toBe(1)
-    expect(mouseup.mock.calls.length).toBe(1)
-  })
-
-  it('object syntax (usage in HOC, mixed with native listeners)', () => {
-    const click = vi.fn()
-    const mouseup = vi.fn()
-    const mousedown = vi.fn()
-
-    vm = new Vue({
-      el,
-      template: `
-        <foo-button
-          @click="click"
-          @mousedown="mousedown"
-          @mouseup.native="mouseup">
-        </foo-button>
-      `,
-      methods: {
-        click,
-        mouseup,
-        mousedown
-      },
-      components: {
-        fooButton: {
-          template: `
-            <button v-on="$listeners"></button>
-          `
-        }
-      }
-    })
-
-    triggerEvent(vm.$el, 'click')
-    expect(click.mock.calls.length).toBe(1)
-    expect(mouseup.mock.calls.length).toBe(0)
-    expect(mousedown.mock.calls.length).toBe(0)
-
-    triggerEvent(vm.$el, 'mouseup')
-    expect(click.mock.calls.length).toBe(1)
-    expect(mouseup.mock.calls.length).toBe(1)
-    expect(mousedown.mock.calls.length).toBe(0)
-
-    triggerEvent(vm.$el, 'mousedown')
-    expect(click.mock.calls.length).toBe(1)
-    expect(mouseup.mock.calls.length).toBe(1)
-    expect(mousedown.mock.calls.length).toBe(1)
-  })
-
-  // #6805 (v-on="object" bind order problem)
-  it('object syntax (no argument): should fire after high-priority listeners', done => {
-    const MyCheckbox = {
-      template: '<input type="checkbox" v-model="model" v-on="$listeners">',
-      props: {
-        value: false
-      },
-      computed: {
-        model: {
-          get() {
-            return this.value
-          },
-          set(val) {
-            this.$emit('input', val)
-          }
-        }
-      }
-    }
-
-    vm = new Vue({
-      el,
-      template: `
-        <div>
-          <my-checkbox v-model="check" @change="change"></my-checkbox>
-        </div>
-      `,
-      components: { MyCheckbox },
-      data: {
-        check: false
-      },
-      methods: {
-        change() {
-          expect(this.check).toBe(true)
-          done()
-        }
-      }
-    })
-
-    vm.$el.querySelector('input').click()
-  })
-
-  it('warn object syntax with modifier', () => {
-    new Vue({
-      template: `<button v-on.self="{}"></button>`
-    }).$mount()
-    expect(
-      `v-on without argument does not support modifiers`
-    ).toHaveBeenWarned()
-  })
-
-  it('warn object syntax with non-object value', () => {
-    new Vue({
-      template: `<button v-on="123"></button>`
-    }).$mount()
-    expect(`v-on without argument expects an Object value`).toHaveBeenWarned()
-  })
-
-  it('should correctly remove once listener', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-if="ok" @click.once="foo">
-            a
-          </span>
-          <span v-else a="a">
-            b
-          </span>
-        </div>
-      `,
-      data: {
-        ok: true
-      },
-      methods: {
-        foo: spy
-      }
-    }).$mount()
-
-    vm.ok = false
-    waitForUpdate(() => {
-      triggerEvent(vm.$el.childNodes[0], 'click')
-      expect(spy.mock.calls.length).toBe(0)
-    }).then(done)
-  })
-
-  // #7628
-  it('handler should return the return value of inline function invocation', () => {
-    let value
-    new Vue({
-      template: `<test @foo="bar()"></test>`,
-      methods: {
-        bar() {
-          return 1
-        }
-      },
-      components: {
-        test: {
-          created() {
-            value = this.$listeners.foo()
-          },
-          render(h) {
-            return h('div')
-          }
-        }
-      }
-    }).$mount()
-    expect(value).toBe(1)
-  })
-
-  it('should not execute callback if modifiers are present', () => {
-    vm = new Vue({
-      el,
-      template: '<input @keyup.?="foo">',
-      methods: { foo: spy }
-    })
-    // simulating autocomplete event (Event object with type keyup but without keyCode)
-    triggerEvent(vm.$el, 'keyup')
-    expect(spy.mock.calls.length).toBe(0)
-  })
-
-  describe('dynamic arguments', () => {
-    it('basic', done => {
-      const spy = vi.fn()
-      const vm = new Vue({
-        template: `<div v-on:[key]="spy"></div>`,
-        data: {
-          key: 'click'
-        },
-        methods: {
-          spy
-        }
-      }).$mount()
-      triggerEvent(vm.$el, 'click')
-      expect(spy.mock.calls.length).toBe(1)
-      vm.key = 'mouseup'
-      waitForUpdate(() => {
-        triggerEvent(vm.$el, 'click')
-        expect(spy.mock.calls.length).toBe(1)
-        triggerEvent(vm.$el, 'mouseup')
-        expect(spy.mock.calls.length).toBe(2)
-        // explicit null value
-        vm.key = null
-      })
-        .then(() => {
-          triggerEvent(vm.$el, 'click')
-          expect(spy.mock.calls.length).toBe(2)
-          triggerEvent(vm.$el, 'mouseup')
-          expect(spy.mock.calls.length).toBe(2)
-        })
-        .then(done)
-    })
-
-    it('shorthand', done => {
-      const spy = vi.fn()
-      const vm = new Vue({
-        template: `<div @[key]="spy"></div>`,
-        data: {
-          key: 'click'
-        },
-        methods: {
-          spy
-        }
-      }).$mount()
-      triggerEvent(vm.$el, 'click')
-      expect(spy.mock.calls.length).toBe(1)
-      vm.key = 'mouseup'
-      waitForUpdate(() => {
-        triggerEvent(vm.$el, 'click')
-        expect(spy.mock.calls.length).toBe(1)
-        triggerEvent(vm.$el, 'mouseup')
-        expect(spy.mock.calls.length).toBe(2)
-      }).then(done)
-    })
-
-    it('with .middle modifier', () => {
-      const spy = vi.fn()
-      const vm = new Vue({
-        template: `<div @[key].middle="spy"></div>`,
-        data: {
-          key: 'click'
-        },
-        methods: {
-          spy
-        }
-      }).$mount()
-      triggerEvent(vm.$el, 'mouseup', e => {
-        e.button = 0
-      })
-      expect(spy).not.toHaveBeenCalled()
-      triggerEvent(vm.$el, 'mouseup', e => {
-        e.button = 1
-      })
-      expect(spy).toHaveBeenCalled()
-    })
-
-    it('with .right modifier', () => {
-      const spy = vi.fn()
-      const vm = new Vue({
-        template: `<div @[key].right="spy"></div>`,
-        data: {
-          key: 'click'
-        },
-        methods: {
-          spy
-        }
-      }).$mount()
-      triggerEvent(vm.$el, 'contextmenu')
-      expect(spy).toHaveBeenCalled()
-    })
-
-    it('with .capture modifier', () => {
-      const callOrder: any[] = []
-      const vm = new Vue({
-        template: `
-          <div @[key].capture="foo">
-            <div @[key]="bar"></div>
-          </div>
-        `,
-        data: {
-          key: 'click'
-        },
-        methods: {
-          foo() {
-            callOrder.push(1)
-          },
-          bar() {
-            callOrder.push(2)
-          }
-        }
-      }).$mount()
-      triggerEvent(vm.$el.firstChild, 'click')
-      expect(callOrder.toString()).toBe('1,2')
-    })
-
-    it('with .once modifier', () => {
-      const vm = new Vue({
-        template: `<div @[key].once="foo"></div>`,
-        data: { key: 'click' },
-        methods: { foo: spy }
-      }).$mount()
-      triggerEvent(vm.$el, 'click')
-      expect(spy.mock.calls.length).toBe(1)
-      triggerEvent(vm.$el, 'click')
-      expect(spy.mock.calls.length).toBe(1) // should no longer trigger
-    })
-  })
-})
diff --git a/test/unit/features/directives/once.spec.ts b/test/unit/features/directives/once.spec.ts
deleted file mode 100644
index 732a440bf6e..00000000000
--- a/test/unit/features/directives/once.spec.ts
+++ /dev/null
@@ -1,386 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-once', () => {
-  it('should not rerender component', done => {
-    const vm = new Vue({
-      template: '<div v-once>{{ a }}</div>',
-      data: { a: 'hello' }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('hello')
-    vm.a = 'world'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('hello')
-    }).then(done)
-  })
-
-  it('should not rerender self and child component', done => {
-    const vm = new Vue({
-      template: `
-        <div v-once>
-          <span>{{ a }}</span>
-          <item :b="a"></item>
-        </div>`,
-      data: { a: 'hello' },
-      components: {
-        item: {
-          template: '<div>{{ b }}</div>',
-          props: ['b']
-        }
-      }
-    }).$mount()
-    expect(vm.$children.length).toBe(1)
-    expect(vm.$el.innerHTML).toBe('<span>hello</span> <div>hello</div>')
-    vm.a = 'world'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<span>hello</span> <div>hello</div>')
-    }).then(done)
-  })
-
-  it('should rerender parent but not self', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span>{{ a }}</span>
-          <item v-once :b="a"></item>
-        </div>`,
-      data: { a: 'hello' },
-      components: {
-        item: {
-          template: '<div>{{ b }}</div>',
-          props: ['b']
-        }
-      }
-    }).$mount()
-    expect(vm.$children.length).toBe(1)
-    expect(vm.$el.innerHTML).toBe('<span>hello</span> <div>hello</div>')
-    vm.a = 'world'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<span>world</span> <div>hello</div>')
-    }).then(done)
-  })
-
-  it('should not rerender static sub nodes', done => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <span v-once>{{ a }}</span>
-          <item :b="a"></item>
-          <span>{{ suffix }}</span>
-        </div>`,
-      data: {
-        a: 'hello',
-        suffix: '?'
-      },
-      components: {
-        item: {
-          template: '<div>{{ b }}</div>',
-          props: ['b']
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      '<span>hello</span> <div>hello</div> <span>?</span>'
-    )
-    vm.a = 'world'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>hello</span> <div>world</div> <span>?</span>'
-      )
-      vm.suffix = '!'
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>hello</span> <div>world</div> <span>!</span>'
-        )
-      })
-      .then(done)
-  })
-
-  it('should work with v-if', done => {
-    const vm = new Vue({
-      data: {
-        tester: true,
-        yes: 'y',
-        no: 'n'
-      },
-      template: `
-        <div>
-          <div v-if="tester">{{ yes }}</div>
-          <div v-else>{{ no }}</div>
-          <div v-if="tester" v-once>{{ yes }}</div>
-          <div v-else>{{ no }}</div>
-          <div v-if="tester">{{ yes }}</div>
-          <div v-else v-once>{{ no }}</div>
-          <div v-if="tester" v-once>{{ yes }}</div>
-          <div v-else v-once>{{ no }}</div>
-        </div>
-      `
-    }).$mount()
-    expectTextContent(vm, 'yyyy')
-    vm.yes = 'yes'
-    waitForUpdate(() => {
-      expectTextContent(vm, 'yesyyesy')
-      vm.tester = false
-    })
-      .then(() => {
-        expectTextContent(vm, 'nnnn')
-        vm.no = 'no'
-      })
-      .then(() => {
-        expectTextContent(vm, 'nononn')
-      })
-      .then(done)
-  })
-
-  it('should work with v-for', done => {
-    const vm = new Vue({
-      data: {
-        list: [1, 2, 3]
-      },
-      template: `<div><div v-for="i in list" v-once>{{i}}</div></div>`
-    }).$mount()
-    expect(vm.$el.textContent).toBe('123')
-    vm.list.reverse()
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('123')
-    }).then(done)
-  })
-
-  it('should work inside v-for', done => {
-    const vm = new Vue({
-      data: {
-        list: [
-          { id: 0, text: 'a' },
-          { id: 1, text: 'b' },
-          { id: 2, text: 'c' }
-        ]
-      },
-      template: `
-        <div>
-          <div v-for="i in list" :key="i.id">
-            <div>
-              <span v-once>{{ i.text }}</span><span>{{ i.text }}</span>
-            </div>
-          </div>
-        </div>
-      `
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('aabbcc')
-
-    vm.list[0].text = 'd'
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('adbbcc')
-      vm.list[1].text = 'e'
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('adbecc')
-        vm.list.reverse()
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('ccbead')
-      })
-      .then(done)
-  })
-
-  it('should work inside v-for with v-if', done => {
-    const vm = new Vue({
-      data: {
-        list: [{ id: 0, text: 'a', tester: true, truthy: 'y' }]
-      },
-      template: `
-        <div>
-          <div v-for="i in list" :key="i.id">
-              <span v-if="i.tester" v-once>{{ i.truthy }}</span>
-              <span v-else v-once>{{ i.text }}</span>
-              <span v-if="i.tester" v-once>{{ i.truthy }}</span>
-              <span v-else>{{ i.text }}</span>
-              <span v-if="i.tester">{{ i.truthy }}</span>
-              <span v-else v-once>{{ i.text }}</span>
-              <span v-if="i.tester">{{ i.truthy }}</span>
-              <span v-else>{{ i.text }}</span>
-          </div>
-        </div>
-      `
-    }).$mount()
-
-    expectTextContent(vm, 'yyyy')
-
-    vm.list[0].truthy = 'yy'
-    waitForUpdate(() => {
-      expectTextContent(vm, 'yyyyyy')
-      vm.list[0].tester = false
-    })
-      .then(() => {
-        expectTextContent(vm, 'aaaa')
-        vm.list[0].text = 'nn'
-      })
-      .then(() => {
-        expectTextContent(vm, 'annann')
-      })
-      .then(done)
-  })
-
-  it('should work inside v-for with nested v-else', done => {
-    const vm = new Vue({
-      data: {
-        list: [{ id: 0, text: 'a', tester: true, truthy: 'y' }]
-      },
-      template: `
-        <div v-if="0"></div>
-        <div v-else>
-          <div v-for="i in list" :key="i.id">
-            <span v-if="i.tester" v-once>{{ i.truthy }}</span>
-            <span v-else v-once>{{ i.text }}</span>
-          </div>
-        </div>
-      `
-    }).$mount()
-
-    expectTextContent(vm, 'y')
-    vm.list[0].truthy = 'yy'
-    waitForUpdate(() => {
-      expectTextContent(vm, 'y')
-      vm.list[0].tester = false
-    })
-      .then(() => {
-        expectTextContent(vm, 'a')
-        vm.list[0].text = 'nn'
-      })
-      .then(() => {
-        expectTextContent(vm, 'a')
-      })
-      .then(done)
-  })
-
-  it('should work inside v-for with nested v-else-if and v-else', done => {
-    const vm = new Vue({
-      data: {
-        tester: false,
-        list: [{ id: 0, text: 'a', tester: true, truthy: 'y' }]
-      },
-      template: `
-        <div v-if="0"></div>
-        <div v-else-if="tester">
-          <div v-for="i in list" :key="i.id">
-            <span v-if="i.tester" v-once>{{ i.truthy }}</span>
-            <span v-else-if="tester" v-once>{{ i.text }}elseif</span>
-            <span v-else v-once>{{ i.text }}</span>
-          </div>
-        </div>
-        <div v-else>
-          <div v-for="i in list" :key="i.id">
-            <span v-if="i.tester" v-once>{{ i.truthy }}</span>
-            <span v-else-if="tester">{{ i.text }}elseif</span>
-            <span v-else v-once>{{ i.text }}</span>
-          </div>
-        </div>
-      `
-    }).$mount()
-
-    expectTextContent(vm, 'y')
-    vm.list[0].truthy = 'yy'
-    waitForUpdate(() => {
-      expectTextContent(vm, 'y')
-      vm.list[0].tester = false
-    })
-      .then(() => {
-        expectTextContent(vm, 'a')
-        vm.list[0].text = 'nn'
-      })
-      .then(() => {
-        expectTextContent(vm, 'a')
-        vm.tester = true
-      })
-      .then(() => {
-        expectTextContent(vm, 'nnelseif')
-        vm.list[0].text = 'xx'
-      })
-      .then(() => {
-        expectTextContent(vm, 'nnelseif')
-        vm.list[0].tester = true
-      })
-      .then(() => {
-        expectTextContent(vm, 'yy')
-        vm.list[0].truthy = 'nn'
-      })
-      .then(() => {
-        expectTextContent(vm, 'yy')
-      })
-      .then(done)
-  })
-
-  it('should warn inside non-keyed v-for', () => {
-    const vm = new Vue({
-      data: {
-        list: [
-          { id: 0, text: 'a' },
-          { id: 1, text: 'b' },
-          { id: 2, text: 'c' }
-        ]
-      },
-      template: `
-        <div>
-          <div v-for="i in list">
-            <span v-once>{{ i.text }}</span><span>{{ i.text }}</span>
-          </div>
-        </div>
-      `
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('aabbcc')
-    expect(
-      `v-once can only be used inside v-for that is keyed.`
-    ).toHaveBeenWarned()
-  })
-
-  // #4288
-  it('should inherit child reference for v-once', done => {
-    const vm = new Vue({
-      template: `<div>{{a}}<test v-if="ok" v-once></test></div>`,
-      data: {
-        a: 0,
-        ok: true
-      },
-      components: {
-        test: {
-          template: '<div>foo</div>'
-        }
-      }
-    }).$mount()
-    vm.a++ // first update to force a patch
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('1foo')
-    })
-      .then(() => {
-        vm.ok = false // teardown component with v-once
-      })
-      .then(done) // should not throw
-  })
-
-  // #6826
-  it('should render different component instances properly', done => {
-    const vm = new Vue({
-      components: {
-        foo: {
-          props: ['name'],
-          template: '<div v-once>{{ name }}</div>'
-        }
-      },
-      template: `
-        <div>
-          <foo name="a" v-once></foo>
-          <foo name="b" v-once></foo>
-        </div>
-      `
-    }).$mount()
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].innerHTML).toBe('a')
-      expect(vm.$el.children[1].innerHTML).toBe('b')
-    }).then(done)
-  })
-})
-
-function expectTextContent(vm, text) {
-  expect(vm.$el.textContent.replace(/\s+/g, '')).toBe(text)
-}
diff --git a/test/unit/features/directives/pre.spec.ts b/test/unit/features/directives/pre.spec.ts
deleted file mode 100644
index 65def9cddb0..00000000000
--- a/test/unit/features/directives/pre.spec.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-pre', function () {
-  it('should not compile inner content', function () {
-    const vm = new Vue({
-      template: `<div>
-        <div v-pre>{{ a }}</div>
-        <div>{{ a }}</div>
-        <div v-pre>
-          <component is="div"></component>
-        </div>
-      </div>`,
-      data: {
-        a: 123
-      }
-    })
-    vm.$mount()
-    expect(vm.$el.firstChild.textContent).toBe('{{ a }}')
-    expect(vm.$el.children[1].textContent).toBe('123')
-    expect(vm.$el.lastChild.innerHTML).toBe('<component is="div"></component>')
-  })
-
-  it('should not compile on root node', function () {
-    const vm = new Vue({
-      template: '<div v-pre>{{ a }}</div>',
-      replace: true,
-      data: {
-        a: 123
-      }
-    })
-    vm.$mount()
-    expect(vm.$el.firstChild.textContent).toBe('{{ a }}')
-  })
-
-  // #8286
-  it('should not compile custom component tags', function () {
-    Vue.component('vtest', { template: ` <div>Hello World</div>` })
-    const vm = new Vue({
-      template: '<div v-pre><vtest></vtest></div>',
-      replace: true
-    })
-    vm.$mount()
-    expect(vm.$el.firstChild.tagName).toBe('VTEST')
-  })
-
-  // #10087
-  it('should not compile attributes', function () {
-    const vm = new Vue({
-      template: '<div v-pre><p open="hello">A Test</p></div>'
-    })
-    vm.$mount()
-    expect(vm.$el.firstChild.getAttribute('open')).toBe('hello')
-  })
-})
diff --git a/test/unit/features/directives/show.spec.ts b/test/unit/features/directives/show.spec.ts
deleted file mode 100644
index 86641b4af4f..00000000000
--- a/test/unit/features/directives/show.spec.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-show', () => {
-  it('should check show value is truthy', () => {
-    const vm = new Vue({
-      template: '<div><span v-show="foo">hello</span></div>',
-      data: { foo: true }
-    }).$mount()
-    expect(vm.$el.firstChild.style.display).toBe('')
-  })
-
-  it('should check show value is falsy', () => {
-    const vm = new Vue({
-      template: '<div><span v-show="foo">hello</span></div>',
-      data: { foo: false }
-    }).$mount()
-    expect(vm.$el.firstChild.style.display).toBe('none')
-  })
-
-  it('should update show value changed', done => {
-    const vm = new Vue({
-      template: '<div><span v-show="foo">hello</span></div>',
-      data: { foo: true }
-    }).$mount()
-    expect(vm.$el.firstChild.style.display).toBe('')
-    vm.foo = false
-    waitForUpdate(() => {
-      expect(vm.$el.firstChild.style.display).toBe('none')
-      vm.foo = {}
-    })
-      .then(() => {
-        expect(vm.$el.firstChild.style.display).toBe('')
-        vm.foo = 0
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.style.display).toBe('none')
-        vm.foo = []
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.style.display).toBe('')
-        vm.foo = null
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.style.display).toBe('none')
-        vm.foo = '0'
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.style.display).toBe('')
-        vm.foo = undefined
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.style.display).toBe('none')
-        vm.foo = 1
-      })
-      .then(() => {
-        expect(vm.$el.firstChild.style.display).toBe('')
-      })
-      .then(done)
-  })
-
-  it('should respect display value in style attribute', done => {
-    const vm = new Vue({
-      template:
-        '<div><span v-show="foo" style="display:block">hello</span></div>',
-      data: { foo: true }
-    }).$mount()
-    expect(vm.$el.firstChild.style.display).toBe('block')
-    vm.foo = false
-    waitForUpdate(() => {
-      expect(vm.$el.firstChild.style.display).toBe('none')
-      vm.foo = true
-    })
-      .then(() => {
-        expect(vm.$el.firstChild.style.display).toBe('block')
-      })
-      .then(done)
-  })
-
-  it('should support unbind when reused', done => {
-    const vm = new Vue({
-      template:
-        '<div v-if="tester"><span v-show="false"></span></div>' +
-        '<div v-else><span @click="tester=!tester">show</span></div>',
-      data: { tester: true }
-    }).$mount()
-    expect(vm.$el.firstChild.style.display).toBe('none')
-    vm.tester = false
-    waitForUpdate(() => {
-      expect(vm.$el.firstChild.style.display).toBe('')
-      vm.tester = true
-    })
-      .then(() => {
-        expect(vm.$el.firstChild.style.display).toBe('none')
-      })
-      .then(done)
-  })
-})
diff --git a/test/unit/features/directives/static-style-parser.spec.ts b/test/unit/features/directives/static-style-parser.spec.ts
deleted file mode 100644
index ccf9e4fe9c1..00000000000
--- a/test/unit/features/directives/static-style-parser.spec.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { parseStyleText } from 'web/util/style'
-const base64ImgUrl =
-  'url("data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==")'
-const logoUrl = 'url(https://vuejs.org/images/logo.png)'
-
-it('should parse normal static style', () => {
-  const staticStyle = `font-size: 12px;background: ${logoUrl};color:red`
-  const res = parseStyleText(staticStyle)
-  expect(res.background).toBe(logoUrl)
-  expect(res.color).toBe('red')
-  expect(res['font-size']).toBe('12px')
-})
-
-it('should parse base64 background', () => {
-  const staticStyle = `background: ${base64ImgUrl}`
-  const res = parseStyleText(staticStyle)
-  expect(res.background).toBe(base64ImgUrl)
-})
-
-it('should parse multiple background images ', () => {
-  let staticStyle = `background: ${logoUrl}, ${logoUrl};`
-  let res = parseStyleText(staticStyle)
-  expect(res.background).toBe(`${logoUrl}, ${logoUrl}`)
-
-  staticStyle = `background: ${base64ImgUrl}, ${base64ImgUrl}`
-  res = parseStyleText(staticStyle)
-  expect(res.background).toBe(`${base64ImgUrl}, ${base64ImgUrl}`)
-})
-
-it('should parse other images ', () => {
-  let staticStyle = `shape-outside: ${logoUrl}`
-  let res = parseStyleText(staticStyle)
-  expect(res['shape-outside']).toBe(logoUrl)
-
-  staticStyle = `list-style-image: ${logoUrl}`
-  res = parseStyleText(staticStyle)
-  expect(res['list-style-image']).toBe(logoUrl)
-
-  staticStyle = `border-image: ${logoUrl} 30 30 repeat`
-  res = parseStyleText(staticStyle)
-  expect(res['border-image']).toBe(`${logoUrl} 30 30 repeat`)
-})
diff --git a/test/unit/features/directives/style.spec.ts b/test/unit/features/directives/style.spec.ts
deleted file mode 100644
index f1cfb871db7..00000000000
--- a/test/unit/features/directives/style.spec.ts
+++ /dev/null
@@ -1,414 +0,0 @@
-import Vue from 'vue'
-
-function checkPrefixedProp(prop) {
-  const el = document.createElement('div')
-  const upper = prop.charAt(0).toUpperCase() + prop.slice(1)
-  if (!(prop in el.style)) {
-    const prefixes = ['Webkit', 'Moz', 'ms']
-    let i = prefixes.length
-    while (i--) {
-      if (prefixes[i] + upper in el.style) {
-        prop = prefixes[i] + upper
-      }
-    }
-  }
-  return prop
-}
-
-describe('Directive v-bind:style', () => {
-  let vm
-
-  beforeEach(() => {
-    vm = new Vue({
-      template: '<div :style="styles"></div>',
-      data() {
-        return {
-          styles: {},
-          fontSize: 16
-        }
-      }
-    }).$mount()
-  })
-
-  it('string', done => {
-    vm.styles = 'color:red;'
-    waitForUpdate(() => {
-      expect(vm.$el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
-    }).then(done)
-  })
-
-  it('falsy number', done => {
-    vm.styles = { opacity: 0 }
-    waitForUpdate(() => {
-      expect(vm.$el.style.opacity).toBe('0')
-    }).then(done)
-  })
-
-  it('plain object', done => {
-    vm.styles = { color: 'red' }
-    waitForUpdate(() => {
-      expect(vm.$el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
-    }).then(done)
-  })
-
-  it('camelCase', done => {
-    vm.styles = { marginRight: '10px' }
-    waitForUpdate(() => {
-      expect(vm.$el.style.marginRight).toBe('10px')
-    }).then(done)
-  })
-
-  it('remove if falsy value', done => {
-    vm.$el.style.color = 'red'
-    waitForUpdate(() => {
-      vm.styles = { color: null }
-    })
-      .then(() => {
-        expect(vm.$el.style.color).toBe('')
-      })
-      .then(done)
-  })
-
-  it('ignore unsupported property', done => {
-    vm.styles = { foo: 'bar' }
-    waitForUpdate(() => {
-      expect(vm.$el.style.foo).not.toBe('bar')
-    }).then(done)
-  })
-
-  it('auto prefix', done => {
-    const prop = checkPrefixedProp('transform')
-    const val = 'scale(0.5)'
-    vm.styles = { transform: val }
-    waitForUpdate(() => {
-      expect(vm.$el.style[prop]).toBe(val)
-    }).then(done)
-  })
-
-  it('auto-prefixed style value as array', done => {
-    vm.styles = { display: ['-webkit-box', '-ms-flexbox', 'flex'] }
-    const testEl = document.createElement('div')
-    vm.styles.display.forEach(value => {
-      testEl.style.display = value
-    })
-    waitForUpdate(() => {
-      expect(vm.$el.style.display).toBe(testEl.style.display)
-    }).then(done)
-  })
-
-  it('!important', done => {
-    vm.styles = { display: 'block !important' }
-    waitForUpdate(() => {
-      expect(vm.$el.style.getPropertyPriority('display')).toBe('important')
-    }).then(done)
-  })
-
-  it('camelCase with !important', done => {
-    vm.styles = { zIndex: '100 !important' }
-    waitForUpdate(() => {
-      expect(vm.$el.style.getPropertyPriority('z-index')).toBe('important')
-    }).then(done)
-  })
-
-  it('object with multiple entries', done => {
-    vm.$el.style.color = 'red'
-    vm.styles = {
-      fontSize: '10px'
-    }
-    waitForUpdate(() => {
-      expect(vm.$el.style.color).toBe('red')
-      expect(vm.$el.style.fontSize).toBe('10px')
-      expect(vm.$el.style.getPropertyValue('font-size')).toBe('10px')
-      vm.styles = {
-        color: 'blue',
-        padding: null
-      }
-    })
-      .then(() => {
-        expect(vm.$el.style.color).toBe('blue')
-        expect(vm.$el.style.padding).toBeFalsy()
-        expect(vm.$el.style.fontSize).toBeFalsy()
-        expect(vm.$el.style.getPropertyValue('font-size')).toBeFalsy()
-        // handle falsy value
-        vm.styles = null
-      })
-      .then(() => {
-        expect(vm.$el.style.color).toBeFalsy()
-        expect(vm.$el.style.padding).toBeFalsy()
-        expect(vm.$el.style.fontSize).toBeFalsy()
-        expect(vm.$el.style.getPropertyValue('font-size')).toBeFalsy()
-      })
-      .then(done)
-  })
-
-  it('array of objects', done => {
-    vm.$el.style.padding = '10px'
-    vm.styles = [{ color: 'red' }, { fontSize: '20px' }]
-
-    waitForUpdate(() => {
-      expect(vm.$el.style.color).toBe('red')
-      expect(vm.$el.style.fontSize).toBe('20px')
-      expect(vm.$el.style.padding).toBe('10px')
-      vm.styles = [{ color: 'blue' }, { padding: null }]
-    })
-      .then(() => {
-        expect(vm.$el.style.color).toBe('blue')
-        expect(vm.$el.style.fontSize).toBeFalsy()
-        expect(vm.$el.style.padding).toBeFalsy()
-      })
-      .then(done)
-  })
-
-  it('updates objects deeply', done => {
-    vm.styles = { display: 'none' }
-    waitForUpdate(() => {
-      expect(vm.$el.style.display).toBe('none')
-      vm.styles.display = 'block'
-    })
-      .then(() => {
-        expect(vm.$el.style.display).toBe('block')
-      })
-      .then(done)
-  })
-
-  it('background size with only one value', done => {
-    vm.styles = { backgroundSize: '100%' }
-    waitForUpdate(() => {
-      expect(vm.$el.style.cssText.replace(/\s/g, '')).toMatch(
-        /background-size:100%(auto)?;/
-      )
-    }).then(done)
-  })
-
-  it('should work with interpolation', done => {
-    vm.styles = { fontSize: `${vm.fontSize}px` }
-    waitForUpdate(() => {
-      expect(vm.$el.style.fontSize).toBe('16px')
-    }).then(done)
-  })
-
-  const supportCssVariable = () => {
-    const el = document.createElement('div')
-    el.style.setProperty('--color', 'red')
-    return el.style.getPropertyValue('--color') === 'red'
-  }
-
-  if (supportCssVariable()) {
-    it('CSS variables', done => {
-      vm.styles = { '--color': 'red' }
-      waitForUpdate(() => {
-        expect(vm.$el.style.getPropertyValue('--color')).toBe('red')
-      }).then(done)
-    })
-  }
-
-  it('should merge static style with binding style', () => {
-    const vm = new Vue({
-      template:
-        '<div style="background: url(https://vuejs.org/images/logo.png);color: blue" :style="test"></div>',
-      data: {
-        test: { color: 'red', fontSize: '12px' }
-      }
-    }).$mount()
-    const style = vm.$el.style
-    expect(style.backgroundImage).toMatch('https://vuejs.org/images/logo.png')
-    expect(style.color).toBe('red')
-    expect(style.fontSize).toBe('12px')
-  })
-
-  it('should merge between parent and child', done => {
-    const vm = new Vue({
-      template:
-        '<child style="text-align: left;margin-right:20px" :style="test"></child>',
-      data: {
-        test: { color: 'red', fontSize: '12px' }
-      },
-      components: {
-        child: {
-          template:
-            '<div style="margin-right:10px;" :style="{marginLeft: marginLeft}"></div>',
-          data: () => ({ marginLeft: '16px' })
-        }
-      }
-    }).$mount()
-    const style = vm.$el.style
-    const child = vm.$children[0]
-    const css = style.cssText.replace(/\s/g, '')
-    expect(css).toContain('margin-right:20px;')
-    expect(css).toContain('margin-left:16px;')
-    expect(css).toContain('text-align:left;')
-    expect(css).toContain('color:red;')
-    expect(css).toContain('font-size:12px;')
-    expect(style.color).toBe('red')
-    expect(style.marginRight).toBe('20px')
-    vm.test.color = 'blue'
-    waitForUpdate(() => {
-      expect(style.color).toBe('blue')
-      child.marginLeft = '30px'
-    })
-      .then(() => {
-        expect(style.marginLeft).toBe('30px')
-        child.fontSize = '30px'
-      })
-      .then(() => {
-        expect(style.fontSize).toBe('12px')
-      })
-      .then(done)
-  })
-
-  it('should not pass to child root element', () => {
-    const vm = new Vue({
-      template: '<child :style="test"></child>',
-      data: {
-        test: { color: 'red', fontSize: '12px' }
-      },
-      components: {
-        child: {
-          template:
-            '<div><nested ref="nested" style="color: blue;text-align:left"></nested></div>',
-          components: {
-            nested: {
-              template: '<div></div>'
-            }
-          }
-        }
-      }
-    }).$mount()
-    const style = vm.$el.style
-    expect(style.color).toBe('red')
-    expect(style.textAlign).toBe('')
-    expect(style.fontSize).toBe('12px')
-    expect(vm.$children[0].$refs.nested.$el.style.color).toBe('blue')
-  })
-
-  it('should merge between nested components', done => {
-    const vm = new Vue({
-      template: '<child :style="test"></child>',
-      data: {
-        test: { color: 'red', fontSize: '12px' }
-      },
-      components: {
-        child: {
-          template: '<nested style="color: blue;text-align:left"></nested>',
-          components: {
-            nested: {
-              template:
-                '<div style="margin-left: 12px;" :style="nestedStyle"></div>',
-              data: () => ({ nestedStyle: { marginLeft: '30px' } })
-            }
-          }
-        }
-      }
-    }).$mount()
-    const style = vm.$el.style
-    const child = vm.$children[0].$children[0]
-    expect(style.color).toBe('red')
-    expect(style.marginLeft).toBe('30px')
-    expect(style.textAlign).toBe('left')
-    expect(style.fontSize).toBe('12px')
-    vm.test.color = 'yellow'
-    waitForUpdate(() => {
-      child.nestedStyle.marginLeft = '60px'
-    })
-      .then(() => {
-        expect(style.marginLeft).toBe('60px')
-        child.nestedStyle = {
-          fontSize: '14px',
-          marginLeft: '40px'
-        }
-      })
-      .then(() => {
-        expect(style.fontSize).toBe('12px')
-        expect(style.marginLeft).toBe('40px')
-      })
-      .then(done)
-  })
-
-  it('should not merge for different adjacent elements', done => {
-    const vm = new Vue({
-      template:
-        '<div>' +
-        '<section style="color: blue" :style="style" v-if="!bool"></section>' +
-        '<div></div>' +
-        '<section style="margin-top: 12px" v-if="bool"></section>' +
-        '</div>',
-      data: {
-        bool: false,
-        style: {
-          fontSize: '12px'
-        }
-      }
-    }).$mount()
-    const style = vm.$el.children[0].style
-    expect(style.fontSize).toBe('12px')
-    expect(style.color).toBe('blue')
-    waitForUpdate(() => {
-      vm.bool = true
-    })
-      .then(() => {
-        expect(style.color).toBe('')
-        expect(style.fontSize).toBe('')
-        expect(style.marginTop).toBe('12px')
-      })
-      .then(done)
-  })
-
-  it('should not merge for v-if, v-else-if and v-else elements', done => {
-    const vm = new Vue({
-      template:
-        '<div>' +
-        '<section style="color: blue" :style="style" v-if="foo"></section>' +
-        '<section style="margin: 12px" v-else-if="bar"></section>' +
-        '<section style="padding: 24px" v-else></section>' +
-        '<div></div>' +
-        '</div>',
-      data: {
-        foo: true,
-        bar: false,
-        style: {
-          fontSize: '12px'
-        }
-      }
-    }).$mount()
-    const style = vm.$el.children[0].style
-    expect(style.fontSize).toBe('12px')
-    expect(style.color).toBe('blue')
-    waitForUpdate(() => {
-      vm.foo = false
-    })
-      .then(() => {
-        expect(style.color).toBe('')
-        expect(style.fontSize).toBe('')
-        expect(style.padding).toBe('24px')
-        vm.bar = true
-      })
-      .then(() => {
-        expect(style.color).toBe('')
-        expect(style.fontSize).toBe('')
-        expect(style.padding).toBe('')
-        expect(style.margin).toBe('12px')
-      })
-      .then(done)
-  })
-
-  // #5318
-  it('should work for elements passed down as a slot', done => {
-    const vm = new Vue({
-      template: `<test><div :style="style"/></test>`,
-      data: {
-        style: { color: 'red' }
-      },
-      components: {
-        test: {
-          template: `<div><slot/></div>`
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.children[0].style.color).toBe('red')
-    vm.style.color = 'green'
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].style.color).toBe('green')
-    }).then(done)
-  })
-})
diff --git a/test/unit/features/directives/text.spec.ts b/test/unit/features/directives/text.spec.ts
deleted file mode 100644
index 5affa3c7f43..00000000000
--- a/test/unit/features/directives/text.spec.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import Vue from 'vue'
-
-describe('Directive v-text', () => {
-  it('should render text', () => {
-    const vm = new Vue({
-      template: '<div v-text="a"></div>',
-      data: { a: 'hello' }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('hello')
-  })
-
-  it('should encode html entities', () => {
-    const vm = new Vue({
-      template: '<div v-text="a"></div>',
-      data: { a: '<foo>' }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('&lt;foo&gt;')
-  })
-
-  it('should support all value types', done => {
-    const vm = new Vue({
-      template: '<div v-text="a"></div>',
-      data: { a: false }
-    }).$mount()
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('false')
-      vm.a = []
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('[]')
-        vm.a = {}
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('{}')
-        vm.a = {
-          toString() {
-            return 'foo'
-          }
-        }
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('foo')
-        vm.a = {
-          toJSON() {
-            return { foo: 'bar' }
-          }
-        }
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('{\n  "foo": "bar"\n}')
-        vm.a = 123
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('123')
-        vm.a = 0
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('0')
-        vm.a = ' '
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(' ')
-        vm.a = '    '
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('    ')
-        vm.a = null
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('')
-        vm.a = undefined
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('')
-      })
-      .then(done)
-  })
-})
diff --git a/test/unit/features/error-handling.spec.ts b/test/unit/features/error-handling.spec.ts
deleted file mode 100644
index b90bb947a96..00000000000
--- a/test/unit/features/error-handling.spec.ts
+++ /dev/null
@@ -1,487 +0,0 @@
-import Vue from 'vue'
-
-const components = createErrorTestComponents()
-
-describe('Error handling', () => {
-  // hooks that prevents the component from rendering, but should not
-  // break parent component
-  ;[
-    ['data', 'data()'],
-    ['render', 'render'],
-    ['beforeCreate', 'beforeCreate hook'],
-    ['created', 'created hook'],
-    ['beforeMount', 'beforeMount hook'],
-    ['directive bind', 'directive foo bind hook'],
-    ['event', 'event handler for "e"']
-  ].forEach(([type, description]) => {
-    it(`should recover from errors in ${type}`, done => {
-      const vm = createTestInstance(components[type])
-      expect(`Error in ${description}`).toHaveBeenWarned()
-      expect(`Error: ${type}`).toHaveBeenWarned()
-      assertRootInstanceActive(vm).then(done)
-    })
-  })
-
-  // hooks that can return rejected promise
-  ;[
-    ['beforeCreate', 'beforeCreate hook'],
-    ['created', 'created hook'],
-    ['beforeMount', 'beforeMount hook'],
-    ['mounted', 'mounted hook'],
-    ['event', 'event handler for "e"']
-  ].forEach(([type, description]) => {
-    it(`should recover from promise errors in ${type}`, done => {
-      createTestInstance(components[`${type}Async`])
-      waitForUpdate(() => {
-        expect(`Error in ${description} (Promise/async)`).toHaveBeenWarned()
-        expect(`Error: ${type}`).toHaveBeenWarned()
-      }).then(done)
-    })
-  })
-
-  // error in mounted hook should affect neither child nor parent
-  it('should recover from errors in mounted hook', done => {
-    const vm = createTestInstance(components.mounted)
-    expect(`Error in mounted hook`).toHaveBeenWarned()
-    expect(`Error: mounted`).toHaveBeenWarned()
-    assertBothInstancesActive(vm).then(done)
-  })
-
-  // error in beforeUpdate/updated should affect neither child nor parent
-  ;[
-    ['beforeUpdate', 'beforeUpdate hook'],
-    ['updated', 'updated hook'],
-    ['directive update', 'directive foo update hook']
-  ].forEach(([type, description]) => {
-    it(`should recover from errors in ${type} hook`, done => {
-      const vm = createTestInstance(components[type])
-      assertBothInstancesActive(vm)
-        .then(() => {
-          expect(`Error in ${description}`).toHaveBeenWarned()
-          expect(`Error: ${type}`).toHaveBeenWarned()
-        })
-        .then(done)
-    })
-  })
-
-  // hooks that can return rejected promise
-  ;[
-    ['beforeUpdate', 'beforeUpdate hook'],
-    ['updated', 'updated hook']
-  ].forEach(([type, description]) => {
-    it(`should recover from promise errors in ${type} hook`, done => {
-      const vm = createTestInstance(components[`${type}Async`])
-      assertBothInstancesActive(vm)
-        .then(() => {
-          expect(`Error in ${description} (Promise/async)`).toHaveBeenWarned()
-          expect(`Error: ${type}`).toHaveBeenWarned()
-        })
-        .then(done)
-    })
-  })
-  ;[
-    ['beforeDestroy', 'beforeDestroy hook'],
-    ['destroyed', 'destroyed hook'],
-    ['directive unbind', 'directive foo unbind hook']
-  ].forEach(([type, description]) => {
-    it(`should recover from errors in ${type} hook`, done => {
-      const vm = createTestInstance(components[type])
-      vm.ok = false
-      waitForUpdate(() => {
-        expect(`Error in ${description}`).toHaveBeenWarned()
-        expect(`Error: ${type}`).toHaveBeenWarned()
-      })
-        .thenWaitFor(next => {
-          assertRootInstanceActive(vm).end(next)
-        })
-        .then(done)
-    })
-  })
-  ;[
-    ['beforeDestroy', 'beforeDestroy hook'],
-    ['destroyed', 'destroyed hook']
-  ].forEach(([type, description]) => {
-    it(`should recover from promise errors in ${type} hook`, done => {
-      const vm = createTestInstance(components[`${type}Async`])
-      vm.ok = false
-      setTimeout(() => {
-        expect(`Error in ${description} (Promise/async)`).toHaveBeenWarned()
-        expect(`Error: ${type}`).toHaveBeenWarned()
-        assertRootInstanceActive(vm).then(done)
-      })
-    })
-  })
-
-  it('should recover from errors in user watcher getter', done => {
-    const vm = createTestInstance(components.userWatcherGetter)
-    vm.n++
-    waitForUpdate(() => {
-      expect(`Error in getter for watcher`).toHaveBeenWarned()
-      function getErrorMsg() {
-        try {
-          this.a.b.c
-        } catch (e: any) {
-          return e.toString()
-        }
-      }
-      const msg = getErrorMsg.call(vm)
-      expect(msg).toHaveBeenWarned()
-    })
-      .thenWaitFor(next => {
-        assertBothInstancesActive(vm).end(next)
-      })
-      .then(done)
-  })
-  ;[
-    ['userWatcherCallback', 'watcher'],
-    ['userImmediateWatcherCallback', 'immediate watcher']
-  ].forEach(([type, description]) => {
-    it(`should recover from errors in user ${description} callback`, done => {
-      const vm = createTestInstance(components[type])
-      assertBothInstancesActive(vm)
-        .then(() => {
-          expect(`Error in callback for ${description} "n"`).toHaveBeenWarned()
-          expect(`Error: ${type} error`).toHaveBeenWarned()
-        })
-        .then(done)
-    })
-
-    it(`should recover from promise errors in user ${description} callback`, done => {
-      const vm = createTestInstance(components[`${type}Async`])
-      assertBothInstancesActive(vm)
-        .then(() => {
-          expect(
-            `Error in callback for ${description} "n" (Promise/async)`
-          ).toHaveBeenWarned()
-          expect(`Error: ${type} error`).toHaveBeenWarned()
-        })
-        .then(done)
-    })
-  })
-
-  it('config.errorHandler should capture render errors', done => {
-    const spy = (Vue.config.errorHandler = vi.fn())
-    const vm = createTestInstance(components.render)
-
-    const args = spy.mock.calls[0]
-    expect(args[0].toString()).toContain('Error: render') // error
-    expect(args[1]).toBe(vm.$refs.child) // vm
-    expect(args[2]).toContain('render') // description
-
-    assertRootInstanceActive(vm)
-      .then(() => {
-        Vue.config.errorHandler = undefined
-      })
-      .then(done)
-  })
-
-  it('should capture and recover from nextTick errors', done => {
-    const err1 = new Error('nextTick')
-    const err2 = new Error('nextTick2')
-    const spy = (Vue.config.errorHandler = vi.fn())
-    Vue.nextTick(() => {
-      throw err1
-    })
-    Vue.nextTick(() => {
-      expect(spy).toHaveBeenCalledWith(err1, undefined, 'nextTick')
-
-      const vm = new Vue()
-      vm.$nextTick(() => {
-        throw err2
-      })
-      Vue.nextTick(() => {
-        // should be called with correct instance info
-        expect(spy).toHaveBeenCalledWith(err2, vm, 'nextTick')
-        Vue.config.errorHandler = undefined
-        done()
-      })
-    })
-  })
-
-  it('should recover from errors thrown in errorHandler itself', () => {
-    Vue.config.errorHandler = () => {
-      throw new Error('error in errorHandler ¯\\_(ツ)_/¯')
-    }
-    const vm = new Vue({
-      render(h) {
-        throw new Error('error in render')
-      },
-      renderError(h, err) {
-        return h('div', err.toString())
-      }
-    }).$mount()
-    expect('error in errorHandler').toHaveBeenWarned()
-    expect('error in render').toHaveBeenWarned()
-    expect(vm.$el.textContent).toContain('error in render')
-    Vue.config.errorHandler = undefined
-  })
-
-  // event handlers that can throw errors or return rejected promise
-  ;[
-    ['single handler', '<div v-on:click="bork"></div>'],
-    [
-      'multiple handlers',
-      '<div v-on="{ click: [bork, function test() {}] }"></div>'
-    ]
-  ].forEach(([type, template]) => {
-    it(`should recover from v-on errors for ${type} registered`, () => {
-      const vm = new Vue({
-        template,
-        methods: {
-          bork() {
-            throw new Error('v-on')
-          }
-        }
-      }).$mount()
-      document.body.appendChild(vm.$el)
-      global.triggerEvent(vm.$el, 'click')
-      expect('Error in v-on handler').toHaveBeenWarned()
-      expect('Error: v-on').toHaveBeenWarned()
-      document.body.removeChild(vm.$el)
-    })
-
-    it(`should recover from v-on async errors for ${type} registered`, done => {
-      const vm = new Vue({
-        template,
-        methods: {
-          bork() {
-            return new Promise((resolve, reject) =>
-              reject(new Error('v-on async'))
-            )
-          }
-        }
-      }).$mount()
-      document.body.appendChild(vm.$el)
-      global.triggerEvent(vm.$el, 'click')
-      waitForUpdate(() => {
-        expect('Error in v-on handler (Promise/async)').toHaveBeenWarned()
-        expect('Error: v-on').toHaveBeenWarned()
-        document.body.removeChild(vm.$el)
-      }).then(done)
-    })
-  })
-})
-
-function createErrorTestComponents() {
-  const components: any = {}
-
-  // data
-  components.data = {
-    data() {
-      throw new Error('data')
-    },
-    render(h) {
-      return h('div')
-    }
-  }
-
-  // render error
-  components.render = {
-    render(h) {
-      throw new Error('render')
-    }
-  }
-
-  // lifecycle errors
-  ;['create', 'mount', 'update', 'destroy'].forEach(hook => {
-    // before
-    const before = 'before' + hook.charAt(0).toUpperCase() + hook.slice(1)
-    const beforeComp = (components[before] = {
-      props: ['n'],
-      render(h) {
-        return h('div', this.n)
-      }
-    })
-    beforeComp[before] = function () {
-      throw new Error(before)
-    }
-
-    const beforeCompAsync = (components[`${before}Async`] = {
-      props: ['n'],
-      render(h) {
-        return h('div', this.n)
-      }
-    })
-    beforeCompAsync[before] = function () {
-      return new Promise((resolve, reject) => reject(new Error(before)))
-    }
-
-    // after
-    const after = hook.replace(/e?$/, 'ed')
-    const afterComp = (components[after] = {
-      props: ['n'],
-      render(h) {
-        return h('div', this.n)
-      }
-    })
-    afterComp[after] = function () {
-      throw new Error(after)
-    }
-
-    const afterCompAsync = (components[`${after}Async`] = {
-      props: ['n'],
-      render(h) {
-        return h('div', this.n)
-      }
-    })
-    afterCompAsync[after] = function () {
-      return new Promise((resolve, reject) => reject(new Error(after)))
-    }
-  })
-
-  // directive hooks errors
-  ;['bind', 'update', 'unbind'].forEach(hook => {
-    const key = 'directive ' + hook
-    const dirComp: any = (components[key] = {
-      props: ['n'],
-      template: `<div v-foo="n">{{ n }}</div>`
-    })
-    const dirFoo = {}
-    dirFoo[hook] = function () {
-      throw new Error(key)
-    }
-    dirComp.directives = {
-      foo: dirFoo
-    }
-  })
-
-  // user watcher
-  components.userWatcherGetter = {
-    props: ['n'],
-    created() {
-      this.$watch(
-        function () {
-          return this.n + this.a.b.c
-        },
-        val => {
-          console.log('user watcher fired: ' + val)
-        }
-      )
-    },
-    render(h) {
-      return h('div', this.n)
-    }
-  }
-
-  components.userWatcherCallback = {
-    props: ['n'],
-    watch: {
-      n() {
-        throw new Error('userWatcherCallback error')
-      }
-    },
-    render(h) {
-      return h('div', this.n)
-    }
-  }
-
-  components.userImmediateWatcherCallback = {
-    props: ['n'],
-    watch: {
-      n: {
-        immediate: true,
-        handler() {
-          throw new Error('userImmediateWatcherCallback error')
-        }
-      }
-    },
-    render(h) {
-      return h('div', this.n)
-    }
-  }
-
-  components.userWatcherCallbackAsync = {
-    props: ['n'],
-    watch: {
-      n() {
-        return Promise.reject(new Error('userWatcherCallback error'))
-      }
-    },
-    render(h) {
-      return h('div', this.n)
-    }
-  }
-
-  components.userImmediateWatcherCallbackAsync = {
-    props: ['n'],
-    watch: {
-      n: {
-        immediate: true,
-        handler() {
-          return Promise.reject(new Error('userImmediateWatcherCallback error'))
-        }
-      }
-    },
-    render(h) {
-      return h('div', this.n)
-    }
-  }
-
-  // event errors
-  components.event = {
-    beforeCreate() {
-      this.$on('e', () => {
-        throw new Error('event')
-      })
-    },
-    mounted() {
-      this.$emit('e')
-    },
-    render(h) {
-      return h('div')
-    }
-  }
-
-  components.eventAsync = {
-    beforeCreate() {
-      this.$on(
-        'e',
-        () => new Promise((resolve, reject) => reject(new Error('event')))
-      )
-    },
-    mounted() {
-      this.$emit('e')
-    },
-    render(h) {
-      return h('div')
-    }
-  }
-
-  return components
-}
-
-function createTestInstance(Comp) {
-  return new Vue({
-    data: {
-      n: 0,
-      ok: true
-    },
-    render(h) {
-      return h('div', [
-        'n:' + this.n + '\n',
-        this.ok ? h(Comp, { ref: 'child', props: { n: this.n } }) : null
-      ])
-    }
-  }).$mount()
-}
-
-function assertRootInstanceActive(vm) {
-  expect(vm.$el.innerHTML).toContain('n:0\n')
-  vm.n++
-  return waitForUpdate(() => {
-    expect(vm.$el.innerHTML).toContain('n:1\n')
-  })
-}
-
-function assertBothInstancesActive(vm) {
-  vm.n = 0
-  return waitForUpdate(() => {
-    expect(vm.$refs.child.$el.innerHTML).toContain('0')
-  }).thenWaitFor(next => {
-    assertRootInstanceActive(vm)
-      .then(() => {
-        expect(vm.$refs.child.$el.innerHTML).toContain('1')
-      })
-      .end(next)
-  })
-}
diff --git a/test/unit/features/filter/filter.spec.ts b/test/unit/features/filter/filter.spec.ts
deleted file mode 100644
index b7d72c93f00..00000000000
--- a/test/unit/features/filter/filter.spec.ts
+++ /dev/null
@@ -1,204 +0,0 @@
-import Vue from 'vue'
-import { parseFilters } from 'compiler/parser/filter-parser'
-
-describe('Filters', () => {
-  it('basic usage', () => {
-    const vm = new Vue({
-      template: '<div>{{ msg | upper }}</div>',
-      data: {
-        msg: 'hi'
-      },
-      filters: {
-        upper: v => v.toUpperCase()
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('HI')
-  })
-
-  it('chained usage', () => {
-    const vm = new Vue({
-      template: '<div>{{ msg | upper | reverse }}</div>',
-      data: {
-        msg: 'hi'
-      },
-      filters: {
-        upper: v => v.toUpperCase(),
-        reverse: v => v.split('').reverse().join('')
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('IH')
-  })
-
-  it('in v-bind', () => {
-    const vm = new Vue({
-      template: `
-        <div
-          v-bind:id="id | upper | reverse"
-          :class="cls | reverse"
-          :ref="ref | lower">
-        </div>
-      `,
-      filters: {
-        upper: v => v.toUpperCase(),
-        reverse: v => v.split('').reverse().join(''),
-        lower: v => v.toLowerCase()
-      },
-      data: {
-        id: 'abc',
-        cls: 'foo',
-        ref: 'BAR'
-      }
-    }).$mount()
-    expect(vm.$el.id).toBe('CBA')
-    expect(vm.$el.className).toBe('oof')
-    expect(vm.$refs.bar).toBe(vm.$el)
-  })
-
-  it('handle regex with pipe', () => {
-    const vm = new Vue({
-      template: `<test ref="test" :pattern="/a|b\\// | identity"></test>`,
-      filters: { identity: v => v },
-      components: {
-        test: {
-          props: ['pattern'],
-          template: '<div></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$refs.test.pattern instanceof RegExp).toBe(true)
-    expect(vm.$refs.test.pattern.toString()).toBe('/a|b\\//')
-  })
-
-  it('handle division', () => {
-    const vm = new Vue({
-      data: { a: 2 },
-      template: `<div>{{ 1/a / 4 | double }}</div>`,
-      filters: { double: v => v * 2 }
-    }).$mount()
-    expect(vm.$el.textContent).toBe(String(1 / 4))
-  })
-
-  it('handle division with parenthesis', () => {
-    const vm = new Vue({
-      data: { a: 20 },
-      template: `<div>{{ (a*2) / 5 | double }}</div>`,
-      filters: { double: v => v * 2 }
-    }).$mount()
-    expect(vm.$el.textContent).toBe(String(16))
-  })
-
-  it('handle division with dot', () => {
-    const vm = new Vue({
-      template: `<div>{{ 20. / 5 | double }}</div>`,
-      filters: { double: v => v * 2 }
-    }).$mount()
-    expect(vm.$el.textContent).toBe(String(8))
-  })
-
-  it('handle division with array values', () => {
-    const vm = new Vue({
-      data: { a: [20] },
-      template: `<div>{{ a[0] / 5 | double }}</div>`,
-      filters: { double: v => v * 2 }
-    }).$mount()
-    expect(vm.$el.textContent).toBe(String(8))
-  })
-
-  it('handle division with hash values', () => {
-    const vm = new Vue({
-      data: { a: { n: 20 } },
-      template: `<div>{{ a['n'] / 5 | double }}</div>`,
-      filters: { double: v => v * 2 }
-    }).$mount()
-    expect(vm.$el.textContent).toBe(String(8))
-  })
-
-  it('handle division with variable_', () => {
-    const vm = new Vue({
-      data: { a_: 8 },
-      template: `<div>{{ a_ / 2 | double }}</div>`,
-      filters: { double: v => v * 2 }
-    }).$mount()
-    expect(vm.$el.textContent).toBe(String(8))
-  })
-
-  it('arguments', () => {
-    const vm = new Vue({
-      template: `<div>{{ msg | add(a, 3) }}</div>`,
-      data: {
-        msg: 1,
-        a: 2
-      },
-      filters: {
-        add: (v, arg1, arg2) => v + arg1 + arg2
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('6')
-  })
-
-  it('quotes', () => {
-    const vm = new Vue({
-      template: `<div>{{ msg + "b | c" + 'd' | upper }}</div>`,
-      data: {
-        msg: 'a'
-      },
-      filters: {
-        upper: v => v.toUpperCase()
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('AB | CD')
-  })
-
-  it('double pipe', () => {
-    const vm = new Vue({
-      template: `<div>{{ b || msg | upper }}</div>`,
-      data: {
-        b: false,
-        msg: 'a'
-      },
-      filters: {
-        upper: v => v.toUpperCase()
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('A')
-  })
-
-  it('object literal', () => {
-    const vm = new Vue({
-      template: `<div>{{ { a: 123 } | pick('a') }}</div>`,
-      filters: {
-        pick: (v, key) => v[key]
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('123')
-  })
-
-  it('array literal', () => {
-    const vm = new Vue({
-      template: `<div>{{ [1, 2, 3] | reverse }}</div>`,
-      filters: {
-        reverse: arr => arr.reverse().join(',')
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('3,2,1')
-  })
-
-  it('warn non-existent', () => {
-    new Vue({
-      template: '<div>{{ msg | upper }}</div>',
-      data: { msg: 'foo' }
-    }).$mount()
-    expect('Failed to resolve filter: upper').toHaveBeenWarned()
-  })
-
-  it('support template string', () => {
-    expect(parseFilters('`a | ${b}c` | d')).toBe('_f("d")(`a | ${b}c`)')
-  })
-
-  it('bigint support', () => {
-    const vm = new Vue({
-      template: `<div>{{ BigInt(BigInt(10000000)) + BigInt(2000000000n) * 3000000n }}</div>`
-    }).$mount()
-    expect(vm.$el.textContent).toBe('6000000010000000')
-  })
-})
diff --git a/test/unit/features/global-api/assets.spec.ts b/test/unit/features/global-api/assets.spec.ts
deleted file mode 100644
index 41e6b5fa6b7..00000000000
--- a/test/unit/features/global-api/assets.spec.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import Vue from 'vue'
-
-describe('Global API: assets', () => {
-  const Test = Vue.extend()
-
-  it('directive / filters', () => {
-    const assets = ['directive', 'filter']
-    assets.forEach(function (type) {
-      const def = {}
-      Test[type]('test', def)
-      expect(Test.options[type + 's'].test).toBe(def)
-      expect(Test[type]('test')).toBe(def)
-      // extended registration should not pollute global
-      expect(Vue.options[type + 's'].test).toBeUndefined()
-    })
-  })
-
-  describe('Vue.component', () => {
-    it('should register a component', () => {
-      Vue.component('foo', {
-        template: '<span>foo</span>'
-      })
-      Vue.component('bar', {
-        template: '<span>bar</span>'
-      })
-      const vm = new Vue({
-        template: '<div><foo></foo><bar></bar></div>'
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe('<span>foo</span><span>bar</span>')
-      // unregister them
-      delete Vue.options.components.foo
-      delete Vue.options.components.bar
-    })
-  })
-
-  it('component on extended constructor', () => {
-    const def = { a: 1 }
-    Test.component('test', def)
-    const component = Test.options.components.test
-    expect(typeof component).toBe('function')
-    expect(component.super).toBe(Vue)
-    expect(component.options.a).toBe(1)
-    expect(component.options.name).toBe('test')
-    expect(Test.component('test')).toBe(component)
-    // already extended
-    Test.component('test2', component)
-    expect(Test.component('test2')).toBe(component)
-    // extended registration should not pollute global
-    expect(Vue.options.components.test).toBeUndefined()
-  })
-
-  // #4434
-  it('local registration should take priority regardless of naming convention', () => {
-    Vue.component('x-foo', {
-      template: '<span>global</span>'
-    })
-    const vm = new Vue({
-      components: {
-        xFoo: {
-          template: '<span>local</span>'
-        }
-      },
-      template: '<div><x-foo></x-foo></div>'
-    }).$mount()
-    expect(vm.$el.textContent).toBe('local')
-    delete Vue.options.components['x-foo']
-  })
-})
diff --git a/test/unit/features/global-api/compile.spec.ts b/test/unit/features/global-api/compile.spec.ts
deleted file mode 100644
index 64ce0ee35f6..00000000000
--- a/test/unit/features/global-api/compile.spec.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import Vue from 'vue'
-
-describe('Global API: compile', () => {
-  it('should compile render functions', () => {
-    const res = Vue.compile('<div><span>{{ msg }}</span></div>')
-    const vm = new Vue({
-      data: {
-        msg: 'hello'
-      },
-      render: res.render,
-      staticRenderFns: res.staticRenderFns
-    }).$mount()
-    expect(vm.$el.innerHTML).toContain('<span>hello</span>')
-  })
-})
diff --git a/test/unit/features/global-api/config.spec.ts b/test/unit/features/global-api/config.spec.ts
deleted file mode 100644
index c9cb6968deb..00000000000
--- a/test/unit/features/global-api/config.spec.ts
+++ /dev/null
@@ -1,125 +0,0 @@
-import Vue from 'vue'
-import { warn } from 'core/util/debug'
-
-describe('Global config', () => {
-  it('should warn replacing config object', () => {
-    const originalConfig = Vue.config
-    Vue.config = {}
-    expect(Vue.config).toBe(originalConfig)
-    expect('Do not replace the Vue.config object').toHaveBeenWarned()
-  })
-
-  describe('silent', () => {
-    it('should be false by default', () => {
-      warn('foo')
-      expect('foo').toHaveBeenWarned()
-    })
-
-    it('should work when set to true', () => {
-      Vue.config.silent = true
-      warn('foo')
-      expect('foo').not.toHaveBeenWarned()
-      Vue.config.silent = false
-    })
-  })
-
-  describe('optionMergeStrategies', () => {
-    it('should allow defining custom option merging strategies', () => {
-      const spy = vi.fn()
-      Vue.config.optionMergeStrategies.__test__ = (parent, child, vm) => {
-        spy(parent, child, vm)
-        return child + 1
-      }
-      const Test = Vue.extend({
-        __test__: 1
-      })
-      expect(spy.mock.calls.length).toBe(1)
-      expect(spy).toHaveBeenCalledWith(undefined, 1, undefined)
-      expect(Test.options.__test__).toBe(2)
-      const test = new Test({
-        __test__: 2
-      })
-      expect(spy.mock.calls.length).toBe(2)
-      expect(spy).toHaveBeenCalledWith(2, 2, test)
-      expect(test.$options.__test__).toBe(3)
-    })
-  })
-
-  describe('ignoredElements', () => {
-    it('should work', () => {
-      Vue.config.ignoredElements = ['foo', /^ion-/]
-      new Vue({
-        template: `<div><foo/><ion-foo/><ion-bar/></div>`
-      }).$mount()
-      expect('Unknown custom element').not.toHaveBeenWarned()
-      Vue.config.ignoredElements = []
-    })
-  })
-
-  describe('async', () => {
-    it('does not update synchronously when true', () => {
-      const spy = vi.fn()
-      const vm = new Vue({
-        template: `<div :class="value"></div>`,
-        updated: spy,
-        data: { value: true }
-      }).$mount()
-      vm.value = false
-      expect(spy).not.toHaveBeenCalled()
-    })
-
-    it('updates synchronously when false', () => {
-      const spy = vi.fn()
-      Vue.config.async = false
-      const vm = new Vue({
-        template: `<div :class="value"></div>`,
-        updated: spy,
-        data: { value: true }
-      }).$mount()
-      vm.value = false
-      expect(spy).toHaveBeenCalled()
-      Vue.config.async = true
-    })
-
-    it('runs watchers in correct order when false', () => {
-      Vue.config.async = false
-      const vm = new Vue({
-        template: `
-          <div id="app">
-            {{ computed }}
-          </div>`,
-        props: ['prop'],
-        propsData: {
-          prop: []
-        },
-        data: () => ({
-          data: ''
-        }),
-        computed: {
-          computed() {
-            return this.prop.join(',')
-          }
-        },
-        watch: {
-          prop: 'execute'
-        },
-        methods: {
-          execute() {
-            this.data = this.computed
-          }
-        }
-      }).$mount()
-      expect(vm.computed).toBe('')
-      expect(vm.data).toBe('')
-
-      vm.prop = [1, 2, 3]
-      expect(vm.computed).toBe('1,2,3')
-      expect(vm.data).toBe('1,2,3')
-
-      vm.prop = [...vm.prop, 4, 5]
-      expect(vm.computed).toBe('1,2,3,4,5')
-      expect(vm.data).toBe('1,2,3,4,5')
-      Vue.config.async = true
-    })
-  })
-})
diff --git a/test/unit/features/global-api/extend.spec.ts b/test/unit/features/global-api/extend.spec.ts
deleted file mode 100644
index b2aca199de4..00000000000
--- a/test/unit/features/global-api/extend.spec.ts
+++ /dev/null
@@ -1,159 +0,0 @@
-import Vue from 'vue'
-
-describe('Global API: extend', () => {
-  it('should correctly merge options', () => {
-    const Test = Vue.extend({
-      name: 'test',
-      a: 1,
-      b: 2
-    })
-    expect(Test.options.a).toBe(1)
-    expect(Test.options.b).toBe(2)
-    expect(Test.super).toBe(Vue)
-    const t = new Test({
-      a: 2
-    })
-    expect(t.$options.a).toBe(2)
-    expect(t.$options.b).toBe(2)
-    // inheritance
-    const Test2 = Test.extend({
-      a: 2
-    })
-    expect(Test2.options.a).toBe(2)
-    expect(Test2.options.b).toBe(2)
-    const t2 = new Test2({
-      a: 3
-    })
-    expect(t2.$options.a).toBe(3)
-    expect(t2.$options.b).toBe(2)
-  })
-
-  it('should warn invalid names', () => {
-    Vue.extend({ name: '123' })
-    expect('Invalid component name: "123"').toHaveBeenWarned()
-    Vue.extend({ name: '_fesf' })
-    expect('Invalid component name: "_fesf"').toHaveBeenWarned()
-    Vue.extend({ name: 'Some App' })
-    expect('Invalid component name: "Some App"').toHaveBeenWarned()
-  })
-
-  it('should work when used as components', () => {
-    const foo = Vue.extend({
-      template: '<span>foo</span>'
-    })
-    const bar = Vue.extend({
-      template: '<span>bar</span>'
-    })
-    const vm = new Vue({
-      template: '<div><foo></foo><bar></bar></div>',
-      components: { foo, bar }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>foo</span><span>bar</span>')
-  })
-
-  it('should merge lifecycle hooks', () => {
-    const calls: any[] = []
-    const A = Vue.extend({
-      created() {
-        calls.push(1)
-      }
-    })
-    const B = A.extend({
-      created() {
-        calls.push(2)
-      }
-    })
-    new B({
-      created() {
-        calls.push(3)
-      }
-    })
-    expect(calls).toEqual([1, 2, 3])
-  })
-
-  it('should not merge nested mixins created with Vue.extend', () => {
-    const A = Vue.extend({
-      created: () => {}
-    })
-    const B = Vue.extend({
-      mixins: [A],
-      created: () => {}
-    })
-    const C = Vue.extend({
-      extends: B,
-      created: () => {}
-    })
-    const D = Vue.extend({
-      mixins: [C],
-      created: () => {}
-    })
-    expect(D.options.created.length).toBe(4)
-  })
-
-  it('should merge methods', () => {
-    const A = Vue.extend({
-      methods: {
-        a() {
-          return this.n
-        }
-      }
-    })
-    const B = A.extend({
-      methods: {
-        b() {
-          return this.n + 1
-        }
-      }
-    })
-    const b = new B({
-      data: { n: 0 },
-      methods: {
-        c() {
-          return this.n + 2
-        }
-      }
-    })
-    expect(b.a()).toBe(0)
-    expect(b.b()).toBe(1)
-    expect(b.c()).toBe(2)
-  })
-
-  it('should merge assets', () => {
-    const A = Vue.extend({
-      components: {
-        aa: {
-          template: '<div>A</div>'
-        }
-      }
-    })
-    const B = A.extend({
-      components: {
-        bb: {
-          template: '<div>B</div>'
-        }
-      }
-    })
-    const b = new B({
-      template: '<div><aa></aa><bb></bb></div>'
-    }).$mount()
-    expect(b.$el.innerHTML).toBe('<div>A</div><div>B</div>')
-  })
-
-  it('caching', () => {
-    const options = {
-      template: '<div></div>'
-    }
-    const A = Vue.extend(options)
-    const B = Vue.extend(options)
-    expect(A).toBe(B)
-  })
-
-  // #4767
-  it('extended options should use different identify from parent', () => {
-    const A = Vue.extend({ computed: {} })
-    const B = A.extend()
-    B.options.computed.b = () => 'foo'
-    expect(B.options.computed).not.toBe(A.options.computed)
-    expect(A.options.computed.b).toBeUndefined()
-  })
-})
diff --git a/test/unit/features/global-api/mixin.spec.ts b/test/unit/features/global-api/mixin.spec.ts
deleted file mode 100644
index 622f8bd806a..00000000000
--- a/test/unit/features/global-api/mixin.spec.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-import Vue from 'vue'
-
-describe('Global API: mixin', () => {
-  let options
-  beforeEach(() => {
-    options = Vue.options
-  })
-  afterEach(() => {
-    Vue.options = options
-  })
-
-  it('should work', () => {
-    const spy = vi.fn()
-    Vue.mixin({
-      created() {
-        spy(this.$options.myOption)
-      }
-    })
-    new Vue({
-      myOption: 'hello'
-    })
-    expect(spy).toHaveBeenCalledWith('hello')
-  })
-
-  it('should work for constructors created before mixin is applied', () => {
-    const calls: any[] = []
-    const Test: Vue = Vue.extend({
-      name: 'test',
-      beforeCreate() {
-        calls.push(this.$options.myOption + ' local')
-      }
-    })
-    Vue.mixin({
-      beforeCreate() {
-        calls.push(this.$options.myOption + ' global')
-      }
-    })
-    expect(Test.options.name).toBe('test')
-    new Test({
-      myOption: 'hello'
-    })
-    expect(calls).toEqual(['hello global', 'hello local'])
-  })
-
-  // #3957
-  it('should work for global props', () => {
-    const Test = Vue.extend({
-      template: `<div>{{ prop }}</div>`
-    })
-
-    Vue.mixin({
-      props: ['prop']
-    })
-
-    // test child component
-    const vm = new Vue({
-      template: '<test prop="hi"></test>',
-      components: { Test }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('hi')
-  })
-
-  // vue-loader#433
-  it('should not drop late-set render functions', () => {
-    const Test = Vue.extend({})
-    Test.options.render = h => h('div', 'hello')
-
-    Vue.mixin({})
-
-    const vm = new Vue({
-      render: h => h(Test)
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('hello')
-  })
-
-  // #4266
-  it('should not drop scopedId', () => {
-    const Test = Vue.extend({})
-    Test.options._scopeId = 'foo'
-
-    Vue.mixin({})
-
-    const vm = new Test({
-      template: '<div><p>hi</p></div>'
-    }).$mount()
-
-    expect(vm.$el.children[0].hasAttribute('foo')).toBe(true)
-  })
-
-  // #4976
-  it('should not drop late-attached custom options on existing constructors', () => {
-    const baseSpy = vi.fn()
-    const Base = Vue.extend({
-      beforeCreate: baseSpy
-    })
-
-    const Test = Base.extend({})
-
-    // Inject options later
-    // vue-loader and vue-hot-reload-api are doing like this
-    Test.options.computed = {
-      $style: () => 123
-    }
-
-    const spy = vi.fn()
-    Test.options.beforeCreate = Test.options.beforeCreate.concat(spy)
-
-    // Update super constructor's options
-    const mixinSpy = vi.fn()
-    Vue.mixin({
-      beforeCreate: mixinSpy
-    })
-
-    // mount the component
-    const vm = new Test({
-      template: '<div>{{ $style }}</div>'
-    }).$mount()
-
-    expect(spy.mock.calls.length).toBe(1)
-    expect(baseSpy.mock.calls.length).toBe(1)
-    expect(mixinSpy.mock.calls.length).toBe(1)
-    expect(vm.$el.textContent).toBe('123')
-    expect(vm.$style).toBe(123)
-
-    // Should not be dropped
-    expect(Test.options.computed.$style()).toBe(123)
-    expect(Test.options.beforeCreate).toEqual([mixinSpy, baseSpy, spy])
-  })
-
-  // vue-class-component#83
-  it('should work for a constructor mixin', () => {
-    const spy = vi.fn()
-    const Mixin = Vue.extend({
-      created() {
-        spy(this.$options.myOption)
-      }
-    })
-
-    Vue.mixin(Mixin)
-
-    new Vue({
-      myOption: 'hello'
-    })
-    expect(spy).toHaveBeenCalledWith('hello')
-  })
-
-  // vue-class-component#87
-  it('should not drop original lifecycle hooks', () => {
-    const base = vi.fn()
-
-    const Base = Vue.extend({
-      beforeCreate: base
-    })
-
-    const injected = vi.fn()
-
-    // inject a function
-    Base.options.beforeCreate = Base.options.beforeCreate.concat(injected)
-
-    Vue.mixin({})
-
-    new Base({})
-
-    expect(base).toHaveBeenCalled()
-    expect(injected).toHaveBeenCalled()
-  })
-
-  // #8595
-  it('chain call', () => {
-    expect(Vue.mixin({})).toBe(Vue)
-  })
-
-  // #9198
-  it('should not mix global mixin lifecycle hook twice', () => {
-    const spy = vi.fn()
-    Vue.mixin({
-      created: spy
-    })
-
-    const mixin1 = Vue.extend({
-      methods: {
-        a() {}
-      }
-    })
-
-    const mixin2 = Vue.extend({
-      mixins: [mixin1]
-    })
-
-    const Child = Vue.extend({
-      mixins: [mixin2]
-    })
-
-    const vm = new Child()
-
-    expect(typeof vm.$options.methods.a).toBe('function')
-    expect(spy.mock.calls.length).toBe(1)
-  })
-})
diff --git a/test/unit/features/global-api/observable.spec.ts b/test/unit/features/global-api/observable.spec.ts
deleted file mode 100644
index 1c0388b889a..00000000000
--- a/test/unit/features/global-api/observable.spec.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import Vue from 'vue'
-
-describe('Global API: observable', () => {
-  it('should work', done => {
-    const state = Vue.observable({
-      count: 0
-    })
-
-    const app = new Vue({
-      render(h) {
-        return h('div', [
-          h('span', state.count),
-          h(
-            'button',
-            {
-              on: {
-                click: () => {
-                  state.count++
-                }
-              }
-            },
-            '+'
-          )
-        ])
-      }
-    }).$mount()
-
-    expect(app.$el.querySelector('span').textContent).toBe('0')
-    app.$el.querySelector('button').click()
-    waitForUpdate(() => {
-      expect(app.$el.querySelector('span').textContent).toBe('1')
-    }).then(done)
-  })
-})
diff --git a/test/unit/features/global-api/set-delete.spec.ts b/test/unit/features/global-api/set-delete.spec.ts
deleted file mode 100644
index 1303119498e..00000000000
--- a/test/unit/features/global-api/set-delete.spec.ts
+++ /dev/null
@@ -1,190 +0,0 @@
-import Vue from 'vue'
-
-describe('Global API: set/delete', () => {
-  describe('Vue.set', () => {
-    it('should update a vue object', done => {
-      const vm = new Vue({
-        template: '<div>{{x}}</div>',
-        data: { x: 1 }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe('1')
-      Vue.set(vm, 'x', 2)
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe('2')
-      }).then(done)
-    })
-
-    it('should update an observing object', done => {
-      const vm = new Vue({
-        template: '<div>{{foo.x}}</div>',
-        data: { foo: { x: 1 } }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe('1')
-      Vue.set(vm.foo, 'x', 2)
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe('2')
-      }).then(done)
-    })
-
-    it('should update an observing array', done => {
-      const vm = new Vue({
-        template: '<div><div v-for="v,k in list">{{k}}-{{v}}</div></div>',
-        data: { list: ['a', 'b', 'c'] }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe(
-        '<div>0-a</div><div>1-b</div><div>2-c</div>'
-      )
-      Vue.set(vm.list, 1, 'd')
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<div>0-a</div><div>1-d</div><div>2-c</div>'
-        )
-        Vue.set(vm.list, '2', 'e')
-      })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<div>0-a</div><div>1-d</div><div>2-e</div>'
-          )
-          /* eslint-disable no-new-wrappers */
-          Vue.set(vm.list, new Number(1), 'f')
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<div>0-a</div><div>1-f</div><div>2-e</div>'
-          )
-          Vue.set(vm.list, '3g', 'g')
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe(
-            '<div>0-a</div><div>1-f</div><div>2-e</div>'
-          )
-        })
-        .then(done)
-    })
-
-    it('should update a vue object with nothing', done => {
-      const vm = new Vue({
-        template: '<div>{{x}}</div>',
-        data: { x: 1 }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe('1')
-      Vue.set(vm, 'x', null)
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe('')
-        Vue.set(vm, 'x')
-      })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('')
-        })
-        .then(done)
-    })
-
-    it('be able to use string type index in array', done => {
-      const vm = new Vue({
-        template: '<div><p v-for="obj in lists">{{obj.name}}</p></div>',
-        data: {
-          lists: [{ name: 'A' }, { name: 'B' }, { name: 'C' }]
-        }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe('<p>A</p><p>B</p><p>C</p>')
-      Vue.set(vm.lists, '0', { name: 'D' })
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe('<p>D</p><p>B</p><p>C</p>')
-      }).then(done)
-    })
-
-    // #6845
-    it('should not overwrite properties on prototype chain', () => {
-      class Model {
-        _bar?: string
-        constructor() {
-          this._bar = null
-        }
-        get bar() {
-          return this._bar
-        }
-        set bar(newvalue) {
-          this._bar = newvalue
-        }
-      }
-
-      const vm = new Vue({
-        data: {
-          data: new Model()
-        }
-      })
-
-      Vue.set(vm.data, 'bar', 123)
-      expect(vm.data.bar).toBe(123)
-      expect(vm.data.hasOwnProperty('bar')).toBe(false)
-      expect(vm.data._bar).toBe(123)
-    })
-  })
-
-  describe('Vue.delete', () => {
-    it('should delete a key', done => {
-      const vm = new Vue({
-        template: '<div>{{obj.x}}</div>',
-        data: { obj: { x: 1 } }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe('1')
-      vm.obj.x = 2
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe('2')
-        Vue.delete(vm.obj, 'x')
-      })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('')
-          vm.obj.x = 3
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('')
-        })
-        .then(done)
-    })
-
-    it('be able to delete an item in array', done => {
-      const vm = new Vue({
-        template: '<div><p v-for="obj in lists">{{obj.name}}</p></div>',
-        data: {
-          lists: [{ name: 'A' }, { name: 'B' }, { name: 'C' }]
-        }
-      }).$mount()
-      expect(vm.$el.innerHTML).toBe('<p>A</p><p>B</p><p>C</p>')
-      Vue.delete(vm.lists, 1)
-      waitForUpdate(() => {
-        expect(vm.$el.innerHTML).toBe('<p>A</p><p>C</p>')
-        Vue.delete(vm.lists, NaN)
-      })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<p>A</p><p>C</p>')
-          Vue.delete(vm.lists, -1)
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<p>A</p><p>C</p>')
-          Vue.delete(vm.lists, '1.3')
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<p>A</p><p>C</p>')
-          Vue.delete(vm.lists, true)
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<p>A</p><p>C</p>')
-          Vue.delete(vm.lists, {})
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<p>A</p><p>C</p>')
-          Vue.delete(vm.lists, '1')
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('<p>A</p>')
-          /* eslint-disable no-new-wrappers */
-          Vue.delete(vm.lists, new Number(0) as number)
-        })
-        .then(() => {
-          expect(vm.$el.innerHTML).toBe('')
-        })
-        .then(done)
-    })
-  })
-})
diff --git a/test/unit/features/global-api/use.spec.ts b/test/unit/features/global-api/use.spec.ts
deleted file mode 100644
index d54ff0750f3..00000000000
--- a/test/unit/features/global-api/use.spec.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import Vue from 'vue'
-
-describe('Global API: use', () => {
-  const def = {}
-  const options = {}
-  const pluginStub = {
-    install: (Vue, opts) => {
-      Vue.directive('plugin-test', def)
-      expect(opts).toBe(options)
-    }
-  }
-
-  it('should apply Object plugin', () => {
-    Vue.use(pluginStub, options)
-    expect(Vue.options.directives['plugin-test']).toBe(def)
-    delete Vue.options.directives['plugin-test']
-    expect(Vue.options.directives['plugin-test']).toBeUndefined()
-
-    // should not double apply
-    Vue.use(pluginStub, options)
-    expect(Vue.options.directives['plugin-test']).toBeUndefined()
-  })
-
-  it('should apply Function plugin', () => {
-    Vue.use(pluginStub.install, options)
-    expect(Vue.options.directives['plugin-test']).toBe(def)
-    delete Vue.options.directives['plugin-test']
-  })
-
-  it('should work on extended constructors without polluting the base', () => {
-    const Ctor = Vue.extend({})
-    Ctor.use(pluginStub, options)
-    expect(Vue.options.directives['plugin-test']).toBeUndefined()
-    expect(Ctor.options.directives['plugin-test']).toBe(def)
-  })
-
-  // GitHub issue #5970
-  it('should work on multi version', () => {
-    const Ctor1 = Vue.extend({})
-    const Ctor2 = Vue.extend({})
-
-    Ctor1.use(pluginStub, options)
-    expect(Vue.options.directives['plugin-test']).toBeUndefined()
-    expect(Ctor1.options.directives['plugin-test']).toBe(def)
-
-    // multi version Vue Ctor with the same cid
-    Ctor2.cid = Ctor1.cid
-    Ctor2.use(pluginStub, options)
-    expect(Vue.options.directives['plugin-test']).toBeUndefined()
-    expect(Ctor2.options.directives['plugin-test']).toBe(def)
-  })
-
-  // #8595
-  it('chain call', () => {
-    expect(Vue.use(() => {})).toBe(Vue)
-  })
-})
diff --git a/test/unit/features/instance/init.spec.ts b/test/unit/features/instance/init.spec.ts
deleted file mode 100644
index 2619a785a6d..00000000000
--- a/test/unit/features/instance/init.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import Vue from 'vue'
-
-describe('Initialization', () => {
-  it('without new', () => {
-    try {
-      Vue()
-    } catch (e) {}
-    expect(
-      'Vue is a constructor and should be called with the `new` keyword'
-    ).toHaveBeenWarned()
-  })
-
-  it('with new', () => {
-    expect(new Vue() instanceof Vue).toBe(true)
-  })
-})
diff --git a/test/unit/features/instance/methods-data.spec.ts b/test/unit/features/instance/methods-data.spec.ts
deleted file mode 100644
index 0cdc4789222..00000000000
--- a/test/unit/features/instance/methods-data.spec.ts
+++ /dev/null
@@ -1,138 +0,0 @@
-import Vue from 'vue'
-
-describe('Instance methods data', () => {
-  it('$set/$delete', done => {
-    const vm = new Vue({
-      template: '<div>{{ a.msg }}</div>',
-      data: {
-        a: {}
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('')
-    vm.$set(vm.a, 'msg', 'hello')
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('hello')
-      vm.$delete(vm.a, 'msg')
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('')
-      })
-      .then(done)
-  })
-
-  describe('$watch', () => {
-    let vm, spy
-    beforeEach(() => {
-      spy = vi.fn()
-      vm = new Vue({
-        data: {
-          a: {
-            b: 1
-          },
-          유니코드: {
-            なまえ: 'ok'
-          }
-        },
-        methods: {
-          foo: spy
-        }
-      })
-    })
-
-    it('basic usage', done => {
-      vm.$watch('a.b', spy)
-      vm.a.b = 2
-      waitForUpdate(() => {
-        expect(spy.mock.calls.length).toBe(1)
-        expect(spy).toHaveBeenCalledWith(2, 1)
-        vm.a = { b: 3 }
-      })
-        .then(() => {
-          expect(spy.mock.calls.length).toBe(2)
-          expect(spy).toHaveBeenCalledWith(3, 2)
-        })
-        .then(done)
-    })
-
-    it('immediate', () => {
-      vm.$watch('a.b', spy, { immediate: true })
-      expect(spy.mock.calls.length).toBe(1)
-      expect(spy).toHaveBeenCalledWith(1)
-    })
-
-    it('unwatch', done => {
-      const unwatch = vm.$watch('a.b', spy)
-      unwatch()
-      vm.a.b = 2
-      waitForUpdate(() => {
-        expect(spy.mock.calls.length).toBe(0)
-      }).then(done)
-    })
-
-    it('function watch', done => {
-      vm.$watch(function () {
-        return this.a.b
-      }, spy)
-      vm.a.b = 2
-      waitForUpdate(() => {
-        expect(spy).toHaveBeenCalledWith(2, 1)
-      }).then(done)
-    })
-
-    it('deep watch', done => {
-      const oldA = vm.a
-      vm.$watch('a', spy, { deep: true })
-      vm.a.b = 2
-      waitForUpdate(() => {
-        expect(spy).toHaveBeenCalledWith(oldA, oldA)
-        vm.a = { b: 3 }
-      })
-        .then(() => {
-          expect(spy).toHaveBeenCalledWith(vm.a, oldA)
-        })
-        .then(done)
-    })
-
-    it('handler option', done => {
-      const oldA = vm.a
-      vm.$watch('a', {
-        handler: spy,
-        deep: true
-      })
-      vm.a.b = 2
-      waitForUpdate(() => {
-        expect(spy).toHaveBeenCalledWith(oldA, oldA)
-        vm.a = { b: 3 }
-      })
-        .then(() => {
-          expect(spy).toHaveBeenCalledWith(vm.a, oldA)
-        })
-        .then(done)
-    })
-
-    it('handler option in string', () => {
-      vm.$watch('a.b', {
-        handler: 'foo',
-        immediate: true
-      })
-      expect(spy.mock.calls.length).toBe(1)
-      expect(spy).toHaveBeenCalledWith(1)
-    })
-
-    it('handler option in string', () => {
-      vm.$watch('유니코드.なまえ', {
-        handler: 'foo',
-        immediate: true
-      })
-      expect(spy.mock.calls.length).toBe(1)
-      expect(spy).toHaveBeenCalledWith('ok')
-    })
-
-    it('warn expression', () => {
-      vm.$watch('a + b', spy)
-      expect(
-        'Watcher only accepts simple dot-delimited paths'
-      ).toHaveBeenWarned()
-    })
-  })
-})
diff --git a/test/unit/features/instance/methods-events.spec.ts b/test/unit/features/instance/methods-events.spec.ts
deleted file mode 100644
index a9dd77b219d..00000000000
--- a/test/unit/features/instance/methods-events.spec.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-import Vue from 'vue'
-
-describe('Instance methods events', () => {
-  let vm, spy
-  beforeEach(() => {
-    vm = new Vue({})
-    spy = vi.fn()
-  })
-
-  it('$on', () => {
-    vm.$on('test', function () {
-      // expect correct context
-      expect(this).toBe(vm)
-      spy.apply(this, arguments)
-    })
-    vm.$emit('test', 1, 2, 3, 4)
-    expect(spy.mock.calls.length).toBe(1)
-    expect(spy).toHaveBeenCalledWith(1, 2, 3, 4)
-  })
-
-  it('$on multi event', () => {
-    vm.$on(['test1', 'test2'], function () {
-      expect(this).toBe(vm)
-      spy.apply(this, arguments)
-    })
-    vm.$emit('test1', 1, 2, 3, 4)
-    expect(spy.mock.calls.length).toBe(1)
-    expect(spy).toHaveBeenCalledWith(1, 2, 3, 4)
-    vm.$emit('test2', 5, 6, 7, 8)
-    expect(spy.mock.calls.length).toBe(2)
-    expect(spy).toHaveBeenCalledWith(5, 6, 7, 8)
-  })
-
-  it('$off multi event', () => {
-    vm.$on(['test1', 'test2', 'test3'], spy)
-    vm.$off(['test1', 'test2'], spy)
-    vm.$emit('test1')
-    vm.$emit('test2')
-    expect(spy).not.toHaveBeenCalled()
-    vm.$emit('test3', 1, 2, 3, 4)
-    expect(spy.mock.calls.length).toBe(1)
-  })
-
-  it('$off multi event without callback', () => {
-    vm.$on(['test1', 'test2'], spy)
-    vm.$off(['test1', 'test2'])
-    vm.$emit('test1')
-    expect(spy).not.toHaveBeenCalled()
-  })
-
-  it('$once', () => {
-    vm.$once('test', spy)
-    vm.$emit('test', 1, 2, 3)
-    vm.$emit('test', 2, 3, 4)
-    expect(spy.mock.calls.length).toBe(1)
-    expect(spy).toHaveBeenCalledWith(1, 2, 3)
-  })
-
-  it('$off event added by $once', () => {
-    vm.$once('test', spy)
-    vm.$off('test', spy) // test off event and this event added by once
-    vm.$emit('test', 1, 2, 3)
-    expect(spy).not.toHaveBeenCalled()
-  })
-
-  it('$off', () => {
-    vm.$on('test1', spy)
-    vm.$on('test2', spy)
-    vm.$off()
-    vm.$emit('test1')
-    vm.$emit('test2')
-    expect(spy).not.toHaveBeenCalled()
-  })
-
-  it('$off event', () => {
-    vm.$on('test1', spy)
-    vm.$on('test2', spy)
-    vm.$off('test1')
-    vm.$off('test1') // test off something that's already off
-    vm.$emit('test1', 1)
-    vm.$emit('test2', 2)
-    expect(spy.mock.calls.length).toBe(1)
-    expect(spy).toHaveBeenCalledWith(2)
-  })
-
-  it('$off event + fn', () => {
-    const spy2 = vi.fn()
-    vm.$on('test', spy)
-    vm.$on('test', spy2)
-    vm.$off('test', spy)
-    vm.$emit('test', 1, 2, 3)
-    expect(spy).not.toHaveBeenCalled()
-    expect(spy2.mock.calls.length).toBe(1)
-    expect(spy2).toHaveBeenCalledWith(1, 2, 3)
-  })
-})
diff --git a/test/unit/features/instance/methods-lifecycle.spec.ts b/test/unit/features/instance/methods-lifecycle.spec.ts
deleted file mode 100644
index 826f1b7a368..00000000000
--- a/test/unit/features/instance/methods-lifecycle.spec.ts
+++ /dev/null
@@ -1,185 +0,0 @@
-import Vue from 'vue'
-import Dep from 'core/observer/dep'
-
-describe('Instance methods lifecycle', () => {
-  describe('$mount', () => {
-    it('empty mount', () => {
-      const vm = new Vue({
-        data: { msg: 'hi' },
-        template: '<div>{{ msg }}</div>'
-      }).$mount()
-      expect(vm.$el.tagName).toBe('DIV')
-      expect(vm.$el.textContent).toBe('hi')
-    })
-
-    it('mount to existing element', () => {
-      const el = document.createElement('div')
-      el.innerHTML = '{{ msg }}'
-      const vm = new Vue({
-        data: { msg: 'hi' }
-      }).$mount(el)
-      expect(vm.$el.tagName).toBe('DIV')
-      expect(vm.$el.textContent).toBe('hi')
-    })
-
-    it('mount to id', () => {
-      const el = document.createElement('div')
-      el.id = 'mount-test'
-      el.innerHTML = '{{ msg }}'
-      document.body.appendChild(el)
-      const vm = new Vue({
-        data: { msg: 'hi' }
-      }).$mount('#mount-test')
-      expect(vm.$el.tagName).toBe('DIV')
-      expect(vm.$el.textContent).toBe('hi')
-    })
-
-    it('Dep.target should be undefined in lifecycle', () => {
-      new Vue({
-        template: '<div><my-component></my-component></div>',
-        components: {
-          myComponent: {
-            template: '<div>hi</div>',
-            mounted() {
-              this.msg
-              expect(Dep.target).toBe(undefined)
-            },
-            computed: {
-              msg() {
-                return 1
-              }
-            }
-          }
-        }
-      }).$mount()
-    })
-
-    it('Dep.target should be undefined during invocation of child immediate watcher', done => {
-      let calls = 0
-      const childData = { a: 1 }
-      const parentUpdate = vi.fn()
-      new Vue({
-        template: '<div><my-component></my-component></div>',
-        updated: parentUpdate,
-        components: {
-          myComponent: {
-            template: '<div>{{ a }}</div>',
-            data() {
-              return childData
-            },
-            watch: {
-              anything: {
-                handler() {
-                  ++calls
-                  this.a
-                },
-                immediate: true
-              }
-            }
-          }
-        }
-      }).$mount()
-      expect(calls).toBe(1)
-      childData.a++
-      waitForUpdate(() => {
-        expect(parentUpdate).not.toHaveBeenCalled()
-      }).then(done)
-    })
-  })
-
-  describe('$destroy', () => {
-    it('remove self from parent', () => {
-      const vm = new Vue({
-        template: '<test></test>',
-        components: {
-          test: { template: '<div></div>' }
-        }
-      }).$mount()
-      vm.$children[0].$destroy()
-      expect(vm.$children.length).toBe(0)
-    })
-
-    it('teardown watchers', () => {
-      const vm = new Vue({
-        data: { a: 123 },
-        template: '<div></div>'
-      }).$mount()
-      vm.$watch('a', () => {})
-      vm.$destroy()
-      expect(vm._watcher.active).toBe(false)
-      expect(vm._scope.effects.every(w => !w.active)).toBe(true)
-    })
-
-    it('remove self from data observer', () => {
-      const vm = new Vue({ data: { a: 1 } })
-      vm.$destroy()
-      expect(vm.$data.__ob__.vmCount).toBe(0)
-    })
-
-    it('avoid duplicate calls', () => {
-      const spy = vi.fn()
-      const vm = new Vue({
-        beforeDestroy: spy
-      })
-      vm.$destroy()
-      vm.$destroy()
-      expect(spy.mock.calls.length).toBe(1)
-    })
-  })
-
-  describe('$forceUpdate', () => {
-    it('should force update', done => {
-      const vm = new Vue({
-        data: {
-          a: {}
-        },
-        template: '<div>{{ a.b }}</div>'
-      }).$mount()
-      expect(vm.$el.textContent).toBe('')
-      vm.a.b = 'foo'
-      waitForUpdate(() => {
-        // should not work because adding new property
-        expect(vm.$el.textContent).toBe('')
-        vm.$forceUpdate()
-      })
-        .then(() => {
-          expect(vm.$el.textContent).toBe('foo')
-        })
-        .then(done)
-    })
-  })
-
-  describe('$nextTick', () => {
-    it('should be called after DOM update in correct context', done => {
-      const vm = new Vue({
-        template: '<div>{{ msg }}</div>',
-        data: {
-          msg: 'foo'
-        }
-      }).$mount()
-      vm.msg = 'bar'
-      vm.$nextTick(function () {
-        expect(this).toBe(vm)
-        expect(vm.$el.textContent).toBe('bar')
-        done()
-      })
-    })
-
-    if (typeof Promise !== 'undefined') {
-      it('should be called after DOM update in correct context, when using Promise syntax', done => {
-        const vm = new Vue({
-          template: '<div>{{ msg }}</div>',
-          data: {
-            msg: 'foo'
-          }
-        }).$mount()
-        vm.msg = 'bar'
-        vm.$nextTick().then(ctx => {
-          expect(ctx).toBe(vm)
-          expect(vm.$el.textContent).toBe('bar')
-          done()
-        })
-      })
-    }
-  })
-})
diff --git a/test/unit/features/instance/properties.spec.ts b/test/unit/features/instance/properties.spec.ts
deleted file mode 100644
index 366997ee22b..00000000000
--- a/test/unit/features/instance/properties.spec.ts
+++ /dev/null
@@ -1,213 +0,0 @@
-import Vue from 'vue'
-
-describe('Instance properties', () => {
-  it('$data', () => {
-    const data = { a: 1 }
-    const vm = new Vue({
-      data
-    })
-    expect(vm.a).toBe(1)
-    expect(vm.$data).toBe(data)
-    // vm -> data
-    vm.a = 2
-    expect(data.a).toBe(2)
-    // data -> vm
-    data.a = 3
-    expect(vm.a).toBe(3)
-  })
-
-  it('$options', () => {
-    const A = Vue.extend({
-      methods: {
-        a() {}
-      }
-    })
-    const vm = new A({
-      methods: {
-        b() {}
-      }
-    })
-    expect(typeof vm.$options.methods?.a).toBe('function')
-    expect(typeof vm.$options.methods?.b).toBe('function')
-  })
-
-  it('$root/$children', done => {
-    const vm = new Vue({
-      template: '<div><test v-if="ok"></test></div>',
-      data: { ok: true },
-      components: {
-        test: {
-          template: '<div></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$root).toBe(vm)
-    expect(vm.$children.length).toBe(1)
-    expect(vm.$children[0].$root).toBe(vm)
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$children.length).toBe(0)
-      vm.ok = true
-    })
-      .then(() => {
-        expect(vm.$children.length).toBe(1)
-        expect(vm.$children[0].$root).toBe(vm)
-      })
-      .then(done)
-  })
-
-  it('$parent', () => {
-    const calls: any[] = []
-    const makeOption = name => ({
-      name,
-      template: `<div><slot></slot></div>`,
-      created() {
-        calls.push(`${name}:${this.$parent.$options.name}`)
-      }
-    })
-    new Vue({
-      template: `
-        <div>
-          <outer><middle><inner></inner></middle></outer>
-          <next></next>
-        </div>
-      `,
-      components: {
-        outer: makeOption('outer'),
-        middle: makeOption('middle'),
-        inner: makeOption('inner'),
-        next: makeOption('next')
-      }
-    }).$mount()
-    expect(calls).toEqual([
-      'outer:undefined',
-      'middle:outer',
-      'inner:middle',
-      'next:undefined'
-    ])
-  })
-
-  it('$props', done => {
-    const Comp = Vue.extend({
-      props: ['msg'],
-      template: '<div>{{ msg }} {{ $props.msg }}</div>'
-    })
-    const vm = new Comp({
-      propsData: {
-        msg: 'foo'
-      }
-    }).$mount()
-    // check render
-    expect(vm.$el.textContent).toContain('foo foo')
-    // warn set
-    vm.$props = {}
-    expect('$props is readonly').toHaveBeenWarned()
-    // check existence
-    expect(vm.$props.msg).toBe('foo')
-    // check change
-    vm.msg = 'bar'
-    expect(vm.$props.msg).toBe('bar')
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toContain('bar bar')
-    })
-      .then(() => {
-        vm.$props.msg = 'baz'
-        expect(vm.msg).toBe('baz')
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toContain('baz baz')
-      })
-      .then(done)
-  })
-
-  it('warn mutating $props', () => {
-    const Comp = {
-      props: ['msg'],
-      render() {},
-      mounted() {
-        expect(this.$props.msg).toBe('foo')
-        this.$props.msg = 'bar'
-      }
-    }
-    new Vue({
-      template: `<comp ref="comp" msg="foo" />`,
-      components: { Comp }
-    }).$mount()
-    expect(`Avoid mutating a prop`).toHaveBeenWarned()
-  })
-
-  it('$attrs', done => {
-    const vm = new Vue({
-      template: `<foo :id="foo" bar="1"/>`,
-      data: { foo: 'foo' },
-      components: {
-        foo: {
-          props: ['bar'],
-          template: `<div><div v-bind="$attrs"></div></div>`
-        }
-      }
-    }).$mount()
-    expect(vm.$el.children[0].id).toBe('foo')
-    expect(vm.$el.children[0].hasAttribute('bar')).toBe(false)
-    vm.foo = 'bar'
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].id).toBe('bar')
-      expect(vm.$el.children[0].hasAttribute('bar')).toBe(false)
-    }).then(done)
-  })
-
-  // #6263
-  it('$attrs should not be undefined when no props passed in', () => {
-    const vm = new Vue({
-      template: `<foo ref="foo" />`,
-      data: { foo: 'foo' },
-      components: {
-        foo: {
-          template: `<div>foo</div>`
-        }
-      }
-    }).$mount()
-    expect(vm.$refs.foo.$attrs).toBeDefined()
-  })
-
-  it('warn mutating $attrs', () => {
-    const vm = new Vue()
-    vm.$attrs = {}
-    expect(`$attrs is readonly`).toHaveBeenWarned()
-  })
-
-  it('$listeners', done => {
-    const spyA = vi.fn()
-    const spyB = vi.fn()
-    const vm = new Vue({
-      template: `<foo @click="foo"/>`,
-      data: { foo: spyA },
-      components: {
-        foo: {
-          template: `<div v-on="$listeners"></div>`
-        }
-      }
-    }).$mount()
-
-    // has to be in dom for test to pass in IE
-    document.body.appendChild(vm.$el)
-
-    triggerEvent(vm.$el, 'click')
-    expect(spyA.mock.calls.length).toBe(1)
-    expect(spyB.mock.calls.length).toBe(0)
-
-    vm.foo = spyB
-    waitForUpdate(() => {
-      triggerEvent(vm.$el, 'click')
-      expect(spyA.mock.calls.length).toBe(1)
-      expect(spyB.mock.calls.length).toBe(1)
-      document.body.removeChild(vm.$el)
-    }).then(done)
-  })
-
-  it('warn mutating $listeners', () => {
-    const vm = new Vue()
-    vm.$listeners = {}
-    expect(`$listeners is readonly`).toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/instance/render-proxy.spec.ts b/test/unit/features/instance/render-proxy.spec.ts
deleted file mode 100644
index 62b347a1292..00000000000
--- a/test/unit/features/instance/render-proxy.spec.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import Vue from 'vue'
-
-if (typeof Proxy !== 'undefined') {
-  describe('render proxy', () => {
-    it('should warn missing property in render fns with `with`', () => {
-      new Vue({
-        template: `<div>{{ a }}</div>`
-      }).$mount()
-      expect(`Property or method "a" is not defined`).toHaveBeenWarned()
-    })
-
-    it('should warn missing property in render fns without `with`', () => {
-      const render = function (h) {
-        return h('div', [this.a])
-      }
-      render._withStripped = true
-      new Vue({
-        render
-      }).$mount()
-      expect(`Property or method "a" is not defined`).toHaveBeenWarned()
-    })
-
-    it('should not warn for hand-written render functions', () => {
-      new Vue({
-        render(h) {
-          return h('div', [this.a])
-        }
-      }).$mount()
-      expect(`Property or method "a" is not defined`).not.toHaveBeenWarned()
-    })
-
-    it('support symbols using the `in` operator in hand-written render functions', () => {
-      const sym = Symbol()
-
-      const vm = new Vue({
-        created() {
-          this[sym] = 'foo'
-        },
-        render(h) {
-          if (sym in this) {
-            return h('div', [this[sym]])
-          }
-        }
-      }).$mount()
-
-      expect(vm.$el.textContent).toBe('foo')
-    })
-
-    it('should warn properties starting with $ when found', () => {
-      new Vue({
-        data: { $a: 'foo' },
-        template: `<div>{{ $a }}</div>`
-      }).$mount()
-      expect(
-        `Property "$a" must be accessed with "$data.$a"`
-      ).toHaveBeenWarned()
-    })
-
-    it('should warn properties starting with _ when found', () => {
-      new Vue({
-        data: { _foo: 'foo' },
-        template: `<div>{{ _foo }}</div>`
-      }).$mount()
-      expect(
-        `Property "_foo" must be accessed with "$data._foo"`
-      ).toHaveBeenWarned()
-    })
-
-    it('should warn properties starting with $ when not found', () => {
-      new Vue({
-        template: `<div>{{ $a }}</div>`
-      }).$mount()
-      expect(`Property or method "$a" is not defined`).toHaveBeenWarned()
-      expect(
-        `Property "$a" must be accessed with "$data.$a"`
-      ).not.toHaveBeenWarned()
-    })
-
-    it('should warn properties starting with $ when not found (with stripped)', () => {
-      const render = function (h) {
-        return h('p', this.$a)
-      }
-      render._withStripped = true
-      new Vue({
-        data: { $a: 'foo' },
-        render
-      }).$mount()
-      expect(
-        `Property "$a" must be accessed with "$data.$a"`
-      ).toHaveBeenWarned()
-    })
-
-    it('should not warn properties starting with $ when using $data to access', () => {
-      new Vue({
-        data: { $a: 'foo' },
-        template: `<div>{{ $data.$a }}</div>`
-      }).$mount()
-      expect(`Property or method "$a" is not defined`).not.toHaveBeenWarned()
-    })
-  })
-}
diff --git a/test/unit/features/options/_scopeId.spec.ts b/test/unit/features/options/_scopeId.spec.ts
deleted file mode 100644
index a800f80f806..00000000000
--- a/test/unit/features/options/_scopeId.spec.ts
+++ /dev/null
@@ -1,98 +0,0 @@
-import Vue from 'vue'
-
-describe('Options _scopeId', () => {
-  it('should add scopeId attributes', () => {
-    const vm = new Vue({
-      _scopeId: 'foo',
-      template: '<div><p><span></span></p></div>'
-    }).$mount()
-    expect(vm.$el.hasAttribute('foo')).toBe(true)
-    expect(vm.$el.children[0].hasAttribute('foo')).toBe(true)
-    expect(vm.$el.children[0].children[0].hasAttribute('foo')).toBe(true)
-  })
-
-  it('should add scopedId attributes from both parent and child on child root', () => {
-    const vm = new Vue({
-      _scopeId: 'foo',
-      template: '<div><child></child></div>',
-      components: {
-        child: {
-          _scopeId: 'bar',
-          template: '<div></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.children[0].hasAttribute('foo')).toBe(true)
-    expect(vm.$el.children[0].hasAttribute('bar')).toBe(true)
-  })
-
-  it('should add scopedId attributes from both parent and child on slot contents', () => {
-    const vm = new Vue({
-      _scopeId: 'foo',
-      template: '<div><child><p>hi</p></child></div>',
-      components: {
-        child: {
-          _scopeId: 'bar',
-          template: '<div><slot></slot></div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.children[0].children[0].hasAttribute('foo')).toBe(true)
-    expect(vm.$el.children[0].children[0].hasAttribute('bar')).toBe(true)
-  })
-
-  // #4774
-  it('should not discard parent scopeId when component root element is replaced', done => {
-    const vm = new Vue({
-      _scopeId: 'data-1',
-      template: `<div><child ref="child" /></div>`,
-      components: {
-        child: {
-          _scopeId: 'data-2',
-          data: () => ({ show: true }),
-          template: '<div v-if="show"></div>'
-        }
-      }
-    }).$mount()
-
-    const child = vm.$refs.child
-
-    expect(child.$el.hasAttribute('data-1')).toBe(true)
-    expect(child.$el.hasAttribute('data-2')).toBe(true)
-
-    child.show = false
-    waitForUpdate(() => {
-      child.show = true
-    })
-      .then(() => {
-        expect(child.$el.hasAttribute('data-1')).toBe(true)
-        expect(child.$el.hasAttribute('data-2')).toBe(true)
-      })
-      .then(done)
-  })
-
-  it('should work on functional components', () => {
-    const child = {
-      functional: true,
-      _scopeId: 'child',
-      render(h) {
-        return h('div', { class: 'child' }, [
-          h('span', { class: 'child' }, 'child')
-        ])
-      }
-    }
-    const vm = new Vue({
-      _scopeId: 'parent',
-      components: { child },
-      template: '<div><child></child></div>'
-    }).$mount()
-
-    expect(vm.$el.hasAttribute('parent')).toBe(true)
-    const childEls = vm.$el.querySelectorAll('.child')
-    ;[].forEach.call(childEls, el => {
-      expect(el.hasAttribute('child')).toBe(true)
-      // functional component with scopeId will not inherit parent scopeId
-      expect(el.hasAttribute('parent')).toBe(false)
-    })
-  })
-})
diff --git a/test/unit/features/options/comments.spec.ts b/test/unit/features/options/comments.spec.ts
deleted file mode 100644
index d1c5fb3de5a..00000000000
--- a/test/unit/features/options/comments.spec.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import Vue from 'vue'
-
-describe('Comments', () => {
-  it('comments should be kept', () => {
-    const vm = new Vue({
-      comments: true,
-      data() {
-        return {
-          foo: 1
-        }
-      },
-      template:
-        '<div><span>node1</span><!--comment1-->{{foo}}<!--comment2--></div>'
-    }).$mount()
-    expect(vm.$el.innerHTML).toEqual(
-      '<span>node1</span><!--comment1-->1<!--comment2-->'
-    )
-  })
-})
diff --git a/test/unit/features/options/components.spec.ts b/test/unit/features/options/components.spec.ts
deleted file mode 100644
index bff289536a5..00000000000
--- a/test/unit/features/options/components.spec.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-import Vue from 'vue'
-import { UA } from 'core/util/env'
-import testObjectOption from '../../../helpers/test-object-option'
-
-describe('Options components', () => {
-  testObjectOption('components')
-
-  it('should accept plain object', () => {
-    const vm = new Vue({
-      template: '<test></test>',
-      components: {
-        test: {
-          template: '<div>hi</div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('DIV')
-    expect(vm.$el.textContent).toBe('hi')
-  })
-
-  it('should accept extended constructor', () => {
-    const Test = Vue.extend({
-      template: '<div>hi</div>'
-    })
-    const vm = new Vue({
-      template: '<test></test>',
-      components: {
-        test: Test
-      }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('DIV')
-    expect(vm.$el.textContent).toBe('hi')
-  })
-
-  it('should accept camelCase', () => {
-    const myComp = {
-      template: '<div>hi</div>'
-    }
-    const vm = new Vue({
-      template: '<my-comp></my-comp>',
-      components: {
-        myComp
-      }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('DIV')
-    expect(vm.$el.textContent).toBe('hi')
-  })
-
-  it('should accept PascalCase', () => {
-    const MyComp = {
-      template: '<div>hi</div>'
-    }
-    const vm = new Vue({
-      template: '<my-comp></my-comp>',
-      components: {
-        MyComp
-      }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('DIV')
-    expect(vm.$el.textContent).toBe('hi')
-  })
-
-  it('should warn native HTML elements', () => {
-    new Vue({
-      components: {
-        div: { template: '<div></div>' }
-      }
-    })
-    expect(
-      'Do not use built-in or reserved HTML elements as component'
-    ).toHaveBeenWarned()
-  })
-
-  it('should warn built-in elements', () => {
-    new Vue({
-      components: {
-        component: { template: '<div></div>' }
-      }
-    })
-    expect(
-      'Do not use built-in or reserved HTML elements as component'
-    ).toHaveBeenWarned()
-  })
-
-  // the HTMLUnknownElement check doesn't work in Android 4.2
-  // but since it doesn't support custom elements nor will any dev use it
-  // as their primary debugging browser, it doesn't really matter.
-  if (!(UA && /android 4\.2/.test(UA))) {
-    it('warn non-existent', () => {
-      new Vue({
-        template: '<test></test>'
-      }).$mount()
-      expect('Unknown custom element: <test>').toHaveBeenWarned()
-    })
-  }
-})
diff --git a/test/unit/features/options/computed.spec.ts b/test/unit/features/options/computed.spec.ts
deleted file mode 100644
index bcebaea4adf..00000000000
--- a/test/unit/features/options/computed.spec.ts
+++ /dev/null
@@ -1,249 +0,0 @@
-import Vue from 'vue'
-import testObjectOption from '../../../helpers/test-object-option'
-
-describe('Options computed', () => {
-  testObjectOption('computed')
-
-  it('basic usage', done => {
-    const vm = new Vue({
-      template: '<div>{{ b }}</div>',
-      data: {
-        a: 1
-      },
-      computed: {
-        b() {
-          return this.a + 1
-        }
-      }
-    }).$mount()
-    expect(vm.b).toBe(2)
-    expect(vm.$el.textContent).toBe('2')
-    vm.a = 2
-    expect(vm.b).toBe(3)
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('3')
-    }).then(done)
-  })
-
-  it('with setter', done => {
-    const vm = new Vue({
-      template: '<div>{{ b }}</div>',
-      data: {
-        a: 1
-      },
-      computed: {
-        b: {
-          get() {
-            return this.a + 1
-          },
-          set(v) {
-            this.a = v - 1
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.b).toBe(2)
-    expect(vm.$el.textContent).toBe('2')
-    vm.a = 2
-    expect(vm.b).toBe(3)
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('3')
-      vm.b = 1
-      expect(vm.a).toBe(0)
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toBe('1')
-      })
-      .then(done)
-  })
-
-  it('warn with setter and no getter', () => {
-    const vm = new Vue({
-      template: `
-        <div>
-          <test></test>
-        </div>
-      `,
-      components: {
-        test: {
-          data() {
-            return {
-              a: 1
-            }
-          },
-          computed: {
-            b: {
-              set(v) {
-                this.a = v
-              }
-            }
-          },
-          template: `<div>{{a}}</div>`
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<div>1</div>')
-    expect('Getter is missing for computed property "b".').toHaveBeenWarned()
-  })
-
-  it('warn assigning to computed with no setter', () => {
-    const vm = new Vue({
-      computed: {
-        b() {
-          return 1
-        }
-      }
-    })
-    vm.b = 2
-    expect(
-      `Computed property "b" was assigned to but it has no setter.`
-    ).toHaveBeenWarned()
-  })
-
-  it('watching computed', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        a: 1
-      },
-      computed: {
-        b() {
-          return this.a + 1
-        }
-      }
-    })
-    vm.$watch('b', spy)
-    vm.a = 2
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(3, 2)
-    }).then(done)
-  })
-
-  it('caching', () => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        a: 1
-      },
-      computed: {
-        b() {
-          spy()
-          return this.a + 1
-        }
-      }
-    })
-    expect(spy.mock.calls.length).toBe(0)
-    vm.b
-    expect(spy.mock.calls.length).toBe(1)
-    vm.b
-    expect(spy.mock.calls.length).toBe(1)
-  })
-
-  it('cache: false', () => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        a: 1
-      },
-      computed: {
-        b: {
-          cache: false,
-          get() {
-            spy()
-            return this.a + 1
-          }
-        }
-      }
-    })
-    expect(spy.mock.calls.length).toBe(0)
-    vm.b
-    expect(spy.mock.calls.length).toBe(1)
-    vm.b
-    expect(spy.mock.calls.length).toBe(2)
-  })
-
-  it('as component', done => {
-    const Comp = Vue.extend({
-      template: `<div>{{ b }} {{ c }}</div>`,
-      data() {
-        return { a: 1 }
-      },
-      computed: {
-        // defined on prototype
-        b() {
-          return this.a + 1
-        }
-      }
-    })
-
-    const vm = new Comp({
-      computed: {
-        // defined at instantiation
-        c() {
-          return this.b + 1
-        }
-      }
-    }).$mount()
-    expect(vm.b).toBe(2)
-    expect(vm.c).toBe(3)
-    expect(vm.$el.textContent).toBe('2 3')
-    vm.a = 2
-    expect(vm.b).toBe(3)
-    expect(vm.c).toBe(4)
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('3 4')
-    }).then(done)
-  })
-
-  it('warn conflict with data', () => {
-    new Vue({
-      data: {
-        a: 1
-      },
-      computed: {
-        a: () => 2
-      }
-    })
-    expect(
-      `computed property "a" is already defined in data`
-    ).toHaveBeenWarned()
-  })
-
-  it('warn conflict with props', () => {
-    new Vue({
-      props: ['a'],
-      propsData: { a: 1 },
-      computed: {
-        a: () => 2
-      }
-    })
-    expect(
-      `computed property "a" is already defined as a prop`
-    ).toHaveBeenWarned()
-  })
-
-  it('warn conflict with methods', () => {
-    new Vue({
-      computed: {
-        a: () => 2
-      },
-      methods: {
-        a: () => {}
-      }
-    })
-    expect(
-      `computed property "a" is already defined as a method`
-    ).toHaveBeenWarned()
-  })
-
-  it('rethrow computed error', () => {
-    const vm = new Vue({
-      computed: {
-        a: () => {
-          throw new Error('rethrow')
-        }
-      }
-    })
-    expect(() => vm.a).toThrowError('rethrow')
-  })
-})
diff --git a/test/unit/features/options/data.spec.ts b/test/unit/features/options/data.spec.ts
deleted file mode 100644
index 398ea70fd23..00000000000
--- a/test/unit/features/options/data.spec.ts
+++ /dev/null
@@ -1,188 +0,0 @@
-import Vue from 'vue'
-
-describe('Options data', () => {
-  it('should proxy and be reactive', done => {
-    const data = { msg: 'foo' }
-    const vm = new Vue({
-      data,
-      template: '<div>{{ msg }}</div>'
-    }).$mount()
-    expect(vm.$data).toEqual({ msg: 'foo' })
-    expect(vm.$data).toBe(data)
-    data.msg = 'bar'
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('bar')
-    }).then(done)
-  })
-
-  it('should merge data properly', () => {
-    const Test = Vue.extend({
-      data() {
-        return { a: 1 }
-      }
-    })
-    let vm = new Test({
-      data: { b: 2 }
-    })
-    expect(vm.a).toBe(1)
-    expect(vm.b).toBe(2)
-    // no instance data
-    vm = new Test()
-    expect(vm.a).toBe(1)
-    // no child-val
-    const Extended = Test.extend({})
-    vm = new Extended()
-    expect(vm.a).toBe(1)
-    // recursively merge objects
-    const WithObject = Vue.extend({
-      data() {
-        return {
-          obj: {
-            a: 1
-          }
-        }
-      }
-    })
-    vm = new WithObject({
-      data: {
-        obj: {
-          b: 2
-        }
-      }
-    })
-    expect(vm.obj.a).toBe(1)
-    expect(vm.obj.b).toBe(2)
-  })
-
-  it('should warn non-function during extend', () => {
-    Vue.extend({
-      data: { msg: 'foo' }
-    })
-    expect('The "data" option should be a function').toHaveBeenWarned()
-  })
-
-  it('should warn non object return', () => {
-    new Vue({
-      data() {}
-    })
-    expect('data functions should return an object').toHaveBeenWarned()
-  })
-
-  it('should warn replacing root $data', () => {
-    const vm = new Vue({
-      data: {}
-    })
-    vm.$data = {}
-    expect('Avoid replacing instance root $data').toHaveBeenWarned()
-  })
-
-  it('should have access to props', () => {
-    const Test = {
-      props: ['a'],
-      render() {},
-      data() {
-        return {
-          b: this.a
-        }
-      }
-    }
-    const vm = new Vue({
-      template: `<test ref="test" :a="1"></test>`,
-      components: { Test }
-    }).$mount()
-    expect(vm.$refs.test.b).toBe(1)
-  })
-
-  it('props should not be reactive', done => {
-    let calls = 0
-    const vm = new Vue({
-      template: `<child :msg="msg"></child>`,
-      data: {
-        msg: 'hello'
-      },
-      beforeUpdate() {
-        calls++
-      },
-      components: {
-        child: {
-          template: `<span>{{ localMsg }}</span>`,
-          props: ['msg'],
-          data() {
-            return { localMsg: this.msg }
-          },
-          computed: {
-            computedMsg() {
-              return this.msg + ' world'
-            }
-          }
-        }
-      }
-    }).$mount()
-    const child = vm.$children[0]
-    vm.msg = 'hi'
-    waitForUpdate(() => {
-      expect(child.localMsg).toBe('hello')
-      expect(child.computedMsg).toBe('hi world')
-      expect(calls).toBe(1)
-    }).then(done)
-  })
-
-  it('should have access to methods', () => {
-    const vm = new Vue({
-      methods: {
-        get() {
-          return { a: 1 }
-        }
-      },
-      data() {
-        return this.get()
-      }
-    })
-    expect(vm.a).toBe(1)
-  })
-
-  it('should be called with this', () => {
-    const vm = new Vue({
-      template: '<div><child></child></div>',
-      provide: { foo: 1 },
-      components: {
-        child: {
-          template: '<span>{{bar}}</span>',
-          inject: ['foo'],
-          data({ foo }) {
-            return { bar: 'foo:' + foo }
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>foo:1</span>')
-  })
-
-  it('should be called with vm as first argument when merged', () => {
-    const superComponent = {
-      data: ({ foo }) => ({ ext: 'ext:' + foo })
-    }
-    const mixins = [
-      {
-        data: ({ foo }) => ({ mixin1: 'm1:' + foo })
-      },
-      {
-        data: ({ foo }) => ({ mixin2: 'm2:' + foo })
-      }
-    ]
-    const vm = new Vue({
-      template: '<div><child></child></div>',
-      provide: { foo: 1 },
-      components: {
-        child: {
-          extends: superComponent,
-          mixins,
-          template: '<span>{{bar}}-{{ext}}-{{mixin1}}-{{mixin2}}</span>',
-          inject: ['foo'],
-          data: ({ foo }) => ({ bar: 'foo:' + foo })
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>foo:1-ext:1-m1:1-m2:1</span>')
-  })
-})
diff --git a/test/unit/features/options/delimiters.spec.ts b/test/unit/features/options/delimiters.spec.ts
deleted file mode 100644
index d477cab0a37..00000000000
--- a/test/unit/features/options/delimiters.spec.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-import Vue from 'vue'
-
-describe('Delimiters', () => {
-  it('default delimiters should work', () => {
-    const vm = new Vue({
-      data: {
-        a: 1
-      },
-      template: '<div>{{ a }}</div>'
-    }).$mount()
-    expect(vm.$el.textContent).toEqual('1')
-  })
-
-  it('custom delimiters should work', () => {
-    const vm = new Vue({
-      delimiters: ['[[', ']]'],
-      template: '<div>[[ a ]]</div>',
-      data: {
-        a: 1
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toEqual('1')
-  })
-
-  it('default delimiters should be ignored when custom delimiters defined', () => {
-    const vm = new Vue({
-      delimiters: ['[[', ']]'],
-      template: '<div>{{ a }}</div>',
-      data: {
-        a: 1
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toEqual('{{ a }}')
-  })
-
-  it('delimiters should only affect vm', () => {
-    const Component = Vue.extend({
-      data: function () {
-        return {
-          b: 2
-        }
-      },
-      template: '<span>[[ b ]]</span>'
-    })
-
-    const vm = new Vue({
-      delimiters: ['[[', ']]'],
-      template: '<div>[[ a ]] - <test-component></test-component></div>',
-      data: {
-        a: 2
-      },
-      components: {
-        'test-component': Component
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toEqual('2 - [[ b ]]')
-  })
-
-  it('delimiters defined globally should work on all vms', () => {
-    Vue.options.delimiters = ['[[', ']]']
-
-    const Component = Vue.extend({
-      template: '<span>[[ a ]]</span>',
-      data: function () {
-        return {
-          a: 2
-        }
-      }
-    })
-
-    const vm = new Vue({
-      data: {
-        b: 1
-      },
-      template: '<div>[[ b ]] - <test-component></test-component></div>',
-      components: {
-        'test-component': Component
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toEqual('1 - 2')
-    // restore default options
-    delete Vue.options.delimiters
-  })
-
-  it('component specific delimiters should override global delimiters', () => {
-    Vue.options.delimiters = ['[[', ']]']
-
-    const Component = Vue.extend({
-      delimiters: ['@{{', '}}'],
-      template: '<span>@{{ a }}</span>',
-      data: function () {
-        return {
-          a: 2
-        }
-      }
-    })
-
-    const vm = new Vue({
-      data: {
-        b: 1
-      },
-      template: '<div>[[ b ]] - <test-component></test-component></div>',
-      components: {
-        'test-component': Component
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toEqual('1 - 2')
-    // restore default options
-    delete Vue.options.delimiters
-  })
-})
diff --git a/test/unit/features/options/directives.spec.ts b/test/unit/features/options/directives.spec.ts
deleted file mode 100644
index 2d999c8b3ae..00000000000
--- a/test/unit/features/options/directives.spec.ts
+++ /dev/null
@@ -1,380 +0,0 @@
-import { SpyInstanceFn } from 'vitest'
-import Vue from 'vue'
-
-describe('Options directives', () => {
-  it('basic usage', done => {
-    const bindSpy = vi.fn()
-    const insertedSpy = vi.fn()
-    const updateSpy = vi.fn()
-    const componentUpdatedSpy = vi.fn()
-    const unbindSpy = vi.fn()
-
-    const assertContext = (el, binding, vnode) => {
-      expect(vnode.context).toBe(vm)
-      expect(binding.arg).toBe('arg')
-      expect(binding.modifiers).toEqual({ hello: true })
-    }
-
-    const vm = new Vue({
-      template:
-        '<div class="hi"><div v-if="ok" v-test:arg.hello="a">{{ msg }}</div></div>',
-      data: {
-        msg: 'hi',
-        a: 'foo',
-        ok: true
-      },
-      directives: {
-        test: {
-          bind(el, binding, vnode) {
-            bindSpy()
-            assertContext(el, binding, vnode)
-            expect(binding.value).toBe('foo')
-            expect(binding.expression).toBe('a')
-            expect(binding.oldValue).toBeUndefined()
-            expect(el.parentNode).toBeNull()
-          },
-          inserted(el, binding, vnode) {
-            insertedSpy()
-            assertContext(el, binding, vnode)
-            expect(binding.value).toBe('foo')
-            expect(binding.expression).toBe('a')
-            expect(binding.oldValue).toBeUndefined()
-            expect(el.parentNode.className).toBe('hi')
-          },
-          update(el, binding, vnode, oldVnode) {
-            updateSpy()
-            assertContext(el, binding, vnode)
-            expect(el).toBe(vm.$el.children[0])
-            expect(oldVnode).not.toBe(vnode)
-            expect(binding.expression).toBe('a')
-            if (binding.value !== binding.oldValue) {
-              expect(binding.value).toBe('bar')
-              expect(binding.oldValue).toBe('foo')
-            }
-          },
-          componentUpdated(el, binding, vnode) {
-            componentUpdatedSpy()
-            assertContext(el, binding, vnode)
-          },
-          unbind(el, binding, vnode) {
-            unbindSpy()
-            assertContext(el, binding, vnode)
-          }
-        }
-      }
-    })
-
-    vm.$mount()
-    expect(bindSpy).toHaveBeenCalled()
-    expect(insertedSpy).toHaveBeenCalled()
-    expect(updateSpy).not.toHaveBeenCalled()
-    expect(componentUpdatedSpy).not.toHaveBeenCalled()
-    expect(unbindSpy).not.toHaveBeenCalled()
-    vm.a = 'bar'
-    waitForUpdate(() => {
-      expect(updateSpy).toHaveBeenCalled()
-      expect(componentUpdatedSpy).toHaveBeenCalled()
-      expect(unbindSpy).not.toHaveBeenCalled()
-      vm.msg = 'bye'
-    })
-      .then(() => {
-        expect(componentUpdatedSpy.mock.calls.length).toBe(2)
-        vm.ok = false
-      })
-      .then(() => {
-        expect(unbindSpy).toHaveBeenCalled()
-      })
-      .then(done)
-  })
-
-  it('function shorthand', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      template: '<div v-test:arg.hello="a"></div>',
-      data: { a: 'foo' },
-      directives: {
-        test(el, binding, vnode) {
-          expect(vnode.context).toBe(vm)
-          expect(binding.arg).toBe('arg')
-          expect(binding.modifiers).toEqual({ hello: true })
-          spy(binding.value, binding.oldValue)
-        }
-      }
-    })
-    vm.$mount()
-    expect(spy).toHaveBeenCalledWith('foo', undefined)
-    vm.a = 'bar'
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith('bar', 'foo')
-    }).then(done)
-  })
-
-  it('function shorthand (global)', done => {
-    const spy = vi.fn()
-    Vue.directive('test', function (el, binding, vnode) {
-      expect(vnode.context).toBe(vm)
-      expect(binding.arg).toBe('arg')
-      expect(binding.modifiers).toEqual({ hello: true })
-      spy(binding.value, binding.oldValue)
-    })
-    const vm = new Vue({
-      template: '<div v-test:arg.hello="a"></div>',
-      data: { a: 'foo' }
-    })
-    vm.$mount()
-    expect(spy).toHaveBeenCalledWith('foo', undefined)
-    vm.a = 'bar'
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith('bar', 'foo')
-      delete Vue.options.directives.test
-    }).then(done)
-  })
-
-  it('should teardown directives on old vnodes when new vnodes have none', done => {
-    const vm = new Vue({
-      data: {
-        ok: true
-      },
-      template: `
-        <div>
-          <div v-if="ok" v-test>a</div>
-          <div v-else class="b">b</div>
-        </div>
-      `,
-      directives: {
-        test: {
-          bind: el => {
-            el.id = 'a'
-          },
-          unbind: el => {
-            el.id = ''
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.children[0].id).toBe('a')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].id).toBe('')
-      expect(vm.$el.children[0].className).toBe('b')
-    }).then(done)
-  })
-
-  it('should properly handle same node with different directive sets', done => {
-    const spies: Record<string, SpyInstanceFn> = {}
-    const createSpy = name => (spies[name] = vi.fn())
-    const vm = new Vue({
-      data: {
-        ok: true,
-        val: 123
-      },
-      template: `
-        <div>
-          <div v-if="ok" v-test="val" v-test.hi="val"></div>
-          <div v-if="!ok" v-test.hi="val" v-test2="val"></div>
-        </div>
-      `,
-      directives: {
-        test: {
-          bind: createSpy('bind1'),
-          inserted: createSpy('inserted1'),
-          update: createSpy('update1'),
-          componentUpdated: createSpy('componentUpdated1'),
-          unbind: createSpy('unbind1')
-        },
-        test2: {
-          bind: createSpy('bind2'),
-          inserted: createSpy('inserted2'),
-          update: createSpy('update2'),
-          componentUpdated: createSpy('componentUpdated2'),
-          unbind: createSpy('unbind2')
-        }
-      }
-    }).$mount()
-
-    expect(spies.bind1.mock.calls.length).toBe(2)
-    expect(spies.inserted1.mock.calls.length).toBe(2)
-    expect(spies.bind2.mock.calls.length).toBe(0)
-    expect(spies.inserted2.mock.calls.length).toBe(0)
-
-    vm.ok = false
-    waitForUpdate(() => {
-      // v-test with modifier should be updated
-      expect(spies.update1.mock.calls.length).toBe(1)
-      expect(spies.componentUpdated1.mock.calls.length).toBe(1)
-
-      // v-test without modifier should be unbound
-      expect(spies.unbind1.mock.calls.length).toBe(1)
-
-      // v-test2 should be bound
-      expect(spies.bind2.mock.calls.length).toBe(1)
-      expect(spies.inserted2.mock.calls.length).toBe(1)
-
-      vm.ok = true
-    })
-      .then(() => {
-        // v-test without modifier should be bound again
-        expect(spies.bind1.mock.calls.length).toBe(3)
-        expect(spies.inserted1.mock.calls.length).toBe(3)
-
-        // v-test2 should be unbound
-        expect(spies.unbind2.mock.calls.length).toBe(1)
-
-        // v-test with modifier should be updated again
-        expect(spies.update1.mock.calls.length).toBe(2)
-        expect(spies.componentUpdated1.mock.calls.length).toBe(2)
-
-        vm.val = 234
-      })
-      .then(() => {
-        expect(spies.update1.mock.calls.length).toBe(4)
-        expect(spies.componentUpdated1.mock.calls.length).toBe(4)
-      })
-      .then(done)
-  })
-
-  it('warn non-existent', () => {
-    new Vue({
-      template: '<div v-test></div>'
-    }).$mount()
-    expect('Failed to resolve directive: test').toHaveBeenWarned()
-  })
-
-  // #6513
-  it('should invoke unbind & inserted on inner component root element change', done => {
-    const dir = {
-      bind: vi.fn(),
-      inserted: vi.fn(),
-      unbind: vi.fn()
-    }
-
-    const Child = {
-      template: `<div v-if="ok"/><span v-else/>`,
-      data: () => ({ ok: true })
-    }
-
-    const vm = new Vue({
-      template: `<child ref="child" v-test />`,
-      directives: { test: dir },
-      components: { Child }
-    }).$mount()
-
-    const oldEl = vm.$el
-    expect(dir.bind.mock.calls.length).toBe(1)
-    expect(dir.bind.mock.calls[0][0]).toBe(oldEl)
-    expect(dir.inserted.mock.calls.length).toBe(1)
-    expect(dir.inserted.mock.calls[0][0]).toBe(oldEl)
-    expect(dir.unbind).not.toHaveBeenCalled()
-
-    vm.$refs.child.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.tagName).toBe('SPAN')
-      expect(dir.bind.mock.calls.length).toBe(2)
-      expect(dir.bind.mock.calls[1][0]).toBe(vm.$el)
-      expect(dir.inserted.mock.calls.length).toBe(2)
-      expect(dir.inserted.mock.calls[1][0]).toBe(vm.$el)
-      expect(dir.unbind.mock.calls.length).toBe(1)
-      expect(dir.unbind.mock.calls[0][0]).toBe(oldEl)
-    }).then(done)
-  })
-
-  it('dynamic arguments', done => {
-    const vm = new Vue({
-      template: `<div v-my:[key]="1"/>`,
-      data: {
-        key: 'foo'
-      },
-      directives: {
-        my: {
-          bind(el, binding) {
-            expect(binding.arg).toBe('foo')
-          },
-          update(el, binding) {
-            expect(binding.arg).toBe('bar')
-            expect(binding.oldArg).toBe('foo')
-            done()
-          }
-        }
-      }
-    }).$mount()
-    vm.key = 'bar'
-  })
-
-  it('deep object like `deep.a` as dynamic arguments', done => {
-    const vm = new Vue({
-      template: `<div v-my:[deep.a]="1"/>`,
-      data: {
-        deep: {
-          a: 'foo'
-        }
-      },
-      directives: {
-        my: {
-          bind(el, binding) {
-            expect(binding.arg).toBe('foo')
-          },
-          update(el, binding) {
-            expect(binding.arg).toBe('bar')
-            expect(binding.oldArg).toBe('foo')
-            done()
-          }
-        }
-      }
-    }).$mount()
-    vm.deep.a = 'bar'
-  })
-
-  it('deep object like `deep.a.b` as dynamic arguments', done => {
-    const vm = new Vue({
-      template: `<div v-my:[deep.a.b]="1"/>`,
-      data: {
-        deep: {
-          a: {
-            b: 'foo'
-          }
-        }
-      },
-      directives: {
-        my: {
-          bind(el, binding) {
-            expect(binding.arg).toBe('foo')
-          },
-          update(el, binding) {
-            expect(binding.arg).toBe('bar')
-            expect(binding.oldArg).toBe('foo')
-            done()
-          }
-        }
-      }
-    }).$mount()
-    vm.deep.a.b = 'bar'
-  })
-
-  it('deep object as dynamic arguments with modifiers', done => {
-    const vm = new Vue({
-      template: `<div v-my:[deep.a.b].x.y="1"/>`,
-      data: {
-        deep: {
-          a: {
-            b: 'foo'
-          }
-        }
-      },
-      directives: {
-        my: {
-          bind(el, binding) {
-            expect(binding.arg).toBe('foo')
-            expect(binding.modifiers.x).toBe(true)
-            expect(binding.modifiers.y).toBe(true)
-          },
-          update(el, binding) {
-            expect(binding.arg).toBe('bar')
-            expect(binding.oldArg).toBe('foo')
-            done()
-          }
-        }
-      }
-    }).$mount()
-    vm.deep.a.b = 'bar'
-  })
-})
diff --git a/test/unit/features/options/el.spec.ts b/test/unit/features/options/el.spec.ts
deleted file mode 100644
index e30382ff02c..00000000000
--- a/test/unit/features/options/el.spec.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import Vue from 'vue'
-
-describe('Options el', () => {
-  it('basic usage', () => {
-    const el = document.createElement('div')
-    el.innerHTML = '<span>{{message}}</span>'
-    const vm = new Vue({
-      el,
-      data: { message: 'hello world' }
-    })
-    expect(vm.$el.tagName).toBe('DIV')
-    expect(vm.$el.textContent).toBe(vm.message)
-  })
-
-  it('should be replaced when use together with `template` option', () => {
-    const el = document.createElement('div')
-    el.innerHTML = '<span>{{message}}</span>'
-    const vm = new Vue({
-      el,
-      template: '<p id="app"><span>{{message}}</span></p>',
-      data: { message: 'hello world' }
-    })
-    expect(vm.$el.tagName).toBe('P')
-    expect(vm.$el.textContent).toBe(vm.message)
-  })
-
-  it('should be replaced when use together with `render` option', () => {
-    const el = document.createElement('div')
-    el.innerHTML = '<span>{{message}}</span>'
-    const vm = new Vue({
-      el,
-      render(h) {
-        return h('p', { staticAttrs: { id: 'app' } }, [
-          h('span', {}, [this.message])
-        ])
-      },
-      data: { message: 'hello world' }
-    })
-    expect(vm.$el.tagName).toBe('P')
-    expect(vm.$el.textContent).toBe(vm.message)
-  })
-
-  it('svg element', () => {
-    const parent = document.createElement('div')
-    parent.innerHTML =
-      '<svg>' +
-      '<text :x="x" :y="y" :fill="color">{{ text }}</text>' +
-      '<g><clipPath><foo></foo></clipPath></g>' +
-      '</svg>'
-    const vm = new Vue({
-      el: parent.childNodes[0],
-      data: {
-        x: 64,
-        y: 128,
-        color: 'red',
-        text: 'svg text'
-      }
-    })
-    expect(vm.$el.tagName).toBe('svg')
-    expect(vm.$el.childNodes[0].getAttribute('x')).toBe(vm.x.toString())
-    expect(vm.$el.childNodes[0].getAttribute('y')).toBe(vm.y.toString())
-    expect(vm.$el.childNodes[0].getAttribute('fill')).toBe(vm.color)
-    expect(vm.$el.childNodes[0].textContent).toBe(vm.text)
-    // nested, non-explicitly listed SVG elements
-    expect(vm.$el.childNodes[1].childNodes[0].namespaceURI).toContain('svg')
-    expect(
-      vm.$el.childNodes[1].childNodes[0].childNodes[0].namespaceURI
-    ).toContain('svg')
-  })
-
-  // https://w3c.github.io/DOM-Parsing/#dfn-serializing-an-attribute-value
-  it('properly decode attribute values when parsing templates from DOM', () => {
-    const el = document.createElement('div')
-    el.innerHTML =
-      '<a href="/a?foo=bar&baz=qux" name="<abc>" single=\'"hi"\'></a>'
-    const vm = new Vue({ el })
-    expect(vm.$el.children[0].getAttribute('href')).toBe('/a?foo=bar&baz=qux')
-    expect(vm.$el.children[0].getAttribute('name')).toBe('<abc>')
-    expect(vm.$el.children[0].getAttribute('single')).toBe('"hi"')
-  })
-
-  it('decode attribute value newlines when parsing templates from DOM in IE', () => {
-    const el = document.createElement('div')
-    el.innerHTML = `<a :style="{\ncolor:'red'\n}"></a>`
-    const vm = new Vue({ el })
-    expect(vm.$el.children[0].style.color).toBe('red')
-  })
-
-  it('warn cannot find element', () => {
-    new Vue({ el: '#non-existent' })
-    expect('Cannot find element: #non-existent').toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/options/errorCaptured.spec.ts b/test/unit/features/options/errorCaptured.spec.ts
deleted file mode 100644
index 65991544e66..00000000000
--- a/test/unit/features/options/errorCaptured.spec.ts
+++ /dev/null
@@ -1,426 +0,0 @@
-import Vue from 'vue'
-
-describe('Options errorCaptured', () => {
-  let globalSpy
-
-  beforeEach(() => {
-    globalSpy = Vue.config.errorHandler = vi.fn()
-  })
-
-  afterEach(() => {
-    Vue.config.errorHandler = undefined
-  })
-
-  it('should capture error from child component', () => {
-    const spy = vi.fn()
-
-    let child
-    let err
-    const Child = {
-      created() {
-        child = this
-        err = new Error('child')
-        throw err
-      },
-      render() {}
-    }
-
-    new Vue({
-      errorCaptured: spy,
-      render: h => h(Child)
-    }).$mount()
-
-    expect(spy).toHaveBeenCalledWith(err, child, 'created hook')
-    // should propagate by default
-    expect(globalSpy).toHaveBeenCalledWith(err, child, 'created hook')
-  })
-
-  it('should be able to render the error in itself', done => {
-    let child
-    const Child = {
-      created() {
-        child = this
-        throw new Error('error from child')
-      },
-      render() {}
-    }
-
-    const vm = new Vue({
-      data: {
-        error: null
-      },
-      errorCaptured(e, vm, info) {
-        expect(vm).toBe(child)
-        this.error = e.toString() + ' in ' + info
-      },
-      render(h) {
-        if (this.error) {
-          return h('pre', this.error)
-        }
-        return h(Child)
-      }
-    }).$mount()
-
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toContain('error from child')
-      expect(vm.$el.textContent).toContain('in created hook')
-    }).then(done)
-  })
-
-  it('should not propagate to global handler when returning true', () => {
-    const spy = vi.fn()
-
-    let child
-    let err
-    const Child = {
-      created() {
-        child = this
-        err = new Error('child')
-        throw err
-      },
-      render() {}
-    }
-
-    new Vue({
-      errorCaptured(err, vm, info) {
-        spy(err, vm, info)
-        return false
-      },
-      render: h => h(Child, {})
-    }).$mount()
-
-    expect(spy).toHaveBeenCalledWith(err, child, 'created hook')
-    // should not propagate
-    expect(globalSpy).not.toHaveBeenCalled()
-  })
-
-  it('should propagate to global handler if itself throws error', () => {
-    let child
-    let err
-    const Child = {
-      created() {
-        child = this
-        err = new Error('child')
-        throw err
-      },
-      render() {}
-    }
-
-    let err2
-    const vm = new Vue({
-      errorCaptured() {
-        err2 = new Error('foo')
-        throw err2
-      },
-      render: h => h(Child, {})
-    }).$mount()
-
-    expect(globalSpy).toHaveBeenCalledWith(err, child, 'created hook')
-    expect(globalSpy).toHaveBeenCalledWith(err2, vm, 'errorCaptured hook')
-  })
-
-  it('should work across multiple parents, mixins and extends', () => {
-    const calls: any[] = []
-
-    const Child = {
-      created() {
-        throw new Error('child')
-      },
-      render() {}
-    }
-
-    const ErrorBoundaryBase = {
-      errorCaptured() {
-        calls.push(1)
-      }
-    }
-
-    const mixin = {
-      errorCaptured() {
-        calls.push(2)
-      }
-    }
-
-    const ErrorBoundaryExtended = {
-      extends: ErrorBoundaryBase,
-      mixins: [mixin],
-      errorCaptured() {
-        calls.push(3)
-      },
-      render: h => h(Child)
-    }
-
-    Vue.config.errorHandler = () => {
-      calls.push(5)
-    }
-
-    new Vue({
-      errorCaptured() {
-        calls.push(4)
-      },
-      render: h => h(ErrorBoundaryExtended)
-    }).$mount()
-
-    expect(calls).toEqual([1, 2, 3, 4, 5])
-  })
-
-  it('should work across multiple parents, mixins and extends with return false', () => {
-    const calls: any[] = []
-
-    const Child = {
-      created() {
-        throw new Error('child')
-      },
-      render() {}
-    }
-
-    const ErrorBoundaryBase = {
-      errorCaptured() {
-        calls.push(1)
-      }
-    }
-
-    const mixin = {
-      errorCaptured() {
-        calls.push(2)
-      }
-    }
-
-    const ErrorBoundaryExtended = {
-      extends: ErrorBoundaryBase,
-      mixins: [mixin],
-      errorCaptured() {
-        calls.push(3)
-        return false
-      },
-      render: h => h(Child)
-    }
-
-    Vue.config.errorHandler = () => {
-      calls.push(5)
-    }
-
-    new Vue({
-      errorCaptured() {
-        calls.push(4)
-      },
-      render: h => h(ErrorBoundaryExtended)
-    }).$mount()
-
-    expect(calls).toEqual([1, 2, 3])
-  })
-
-  // ref: https://github.com/vuejs/vuex/issues/1505
-  it('should not add watchers to render deps if they are referred from errorCaptured callback', done => {
-    const store = new Vue({
-      data: {
-        errors: []
-      }
-    })
-
-    const Child = {
-      computed: {
-        test() {
-          throw new Error('render error')
-        }
-      },
-
-      render(h) {
-        return h('div', {
-          attrs: {
-            'data-test': this.test
-          }
-        })
-      }
-    }
-
-    new Vue({
-      errorCaptured(error) {
-        store.errors.push(error)
-      },
-      render: h => h(Child)
-    }).$mount()
-
-    // Ensure not to trigger infinite loop
-    waitForUpdate(() => {
-      expect(store.errors.length).toBe(1)
-      expect(store.errors[0]).toEqual(new Error('render error'))
-    }).then(done)
-  })
-
-  it('should capture error from watcher', done => {
-    const spy = vi.fn()
-
-    let child
-    let err
-    const Child = {
-      data() {
-        return {
-          foo: null
-        }
-      },
-      watch: {
-        foo() {
-          err = new Error('userWatcherCallback error')
-          throw err
-        }
-      },
-      created() {
-        child = this
-      },
-      render() {}
-    }
-
-    new Vue({
-      errorCaptured: spy,
-      render: h => h(Child)
-    }).$mount()
-
-    child.foo = 'bar'
-
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(err, child, 'callback for watcher "foo"')
-      expect(globalSpy).toHaveBeenCalledWith(
-        err,
-        child,
-        'callback for watcher "foo"'
-      )
-    }).then(done)
-  })
-
-  it('should capture promise error from watcher', done => {
-    const spy = vi.fn()
-
-    let child
-    let err
-    const Child = {
-      data() {
-        return {
-          foo: null
-        }
-      },
-      watch: {
-        foo() {
-          err = new Error('userWatcherCallback error')
-          return Promise.reject(err)
-        }
-      },
-      created() {
-        child = this
-      },
-      render() {}
-    }
-
-    new Vue({
-      errorCaptured: spy,
-      render: h => h(Child)
-    }).$mount()
-
-    child.foo = 'bar'
-
-    child.$nextTick(() => {
-      waitForUpdate(() => {
-        expect(spy).toHaveBeenCalledWith(
-          err,
-          child,
-          'callback for watcher "foo" (Promise/async)'
-        )
-        expect(globalSpy).toHaveBeenCalledWith(
-          err,
-          child,
-          'callback for watcher "foo" (Promise/async)'
-        )
-      }).then(done)
-    })
-  })
-
-  it('should capture error from immediate watcher', done => {
-    const spy = vi.fn()
-
-    let child
-    let err
-    const Child = {
-      data() {
-        return {
-          foo: 'foo'
-        }
-      },
-      watch: {
-        foo: {
-          immediate: true,
-          handler() {
-            err = new Error('userImmediateWatcherCallback error')
-            throw err
-          }
-        }
-      },
-      created() {
-        child = this
-      },
-      render() {}
-    }
-
-    new Vue({
-      errorCaptured: spy,
-      render: h => h(Child)
-    }).$mount()
-
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(
-        err,
-        child,
-        'callback for immediate watcher "foo"'
-      )
-      expect(globalSpy).toHaveBeenCalledWith(
-        err,
-        child,
-        'callback for immediate watcher "foo"'
-      )
-    }).then(done)
-  })
-
-  it('should capture promise error from immediate watcher', done => {
-    const spy = vi.fn()
-
-    let child
-    let err
-    const Child = {
-      data() {
-        return {
-          foo: 'foo'
-        }
-      },
-      watch: {
-        foo: {
-          immediate: true,
-          handler() {
-            err = new Error('userImmediateWatcherCallback error')
-            return Promise.reject(err)
-          }
-        }
-      },
-      created() {
-        child = this
-      },
-      render() {}
-    }
-
-    new Vue({
-      errorCaptured: spy,
-      render: h => h(Child)
-    }).$mount()
-
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(
-        err,
-        child,
-        'callback for immediate watcher "foo" (Promise/async)'
-      )
-      expect(globalSpy).toHaveBeenCalledWith(
-        err,
-        child,
-        'callback for immediate watcher "foo" (Promise/async)'
-      )
-    }).then(done)
-  })
-})
diff --git a/test/unit/features/options/extends.spec.ts b/test/unit/features/options/extends.spec.ts
deleted file mode 100644
index d94247dd540..00000000000
--- a/test/unit/features/options/extends.spec.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import Vue from 'vue'
-import { nativeWatch } from 'core/util/env'
-
-describe('Options extends', () => {
-  it('should work on objects', () => {
-    const A = {
-      data() {
-        return { a: 1 }
-      }
-    }
-    const B = {
-      extends: A,
-      data() {
-        return { b: 2 }
-      }
-    }
-    const vm = new Vue({
-      extends: B,
-      data: {
-        c: 3
-      }
-    })
-    expect(vm.a).toBe(1)
-    expect(vm.b).toBe(2)
-    expect(vm.c).toBe(3)
-  })
-
-  it('should work on extended constructors', () => {
-    const A = Vue.extend({
-      data() {
-        return { a: 1 }
-      }
-    })
-    const B = Vue.extend({
-      extends: A,
-      data() {
-        return { b: 2 }
-      }
-    })
-    const vm = new Vue({
-      extends: B,
-      data: {
-        c: 3
-      }
-    })
-    expect(vm.a).toBe(1)
-    expect(vm.b).toBe(2)
-    expect(vm.c).toBe(3)
-  })
-
-  if (nativeWatch) {
-    it('should work with global mixins + Object.prototype.watch', done => {
-      Vue.mixin({})
-
-      const spy = vi.fn()
-      const A = Vue.extend({
-        data: function () {
-          return { a: 1 }
-        },
-        watch: {
-          a: spy
-        },
-        created: function () {
-          this.a = 2
-        }
-      })
-      new Vue({
-        extends: A
-      })
-      waitForUpdate(() => {
-        expect(spy).toHaveBeenCalledWith(2, 1)
-      }).then(done)
-    })
-  }
-})
diff --git a/test/unit/features/options/functional.spec.ts b/test/unit/features/options/functional.spec.ts
deleted file mode 100644
index c26930b203c..00000000000
--- a/test/unit/features/options/functional.spec.ts
+++ /dev/null
@@ -1,369 +0,0 @@
-import Vue from 'vue'
-import { createEmptyVNode } from 'core/vdom/vnode'
-
-describe('Options functional', () => {
-  it('should work', done => {
-    const vm = new Vue({
-      data: { test: 'foo' },
-      template: '<div><wrap :msg="test">bar</wrap></div>',
-      components: {
-        wrap: {
-          functional: true,
-          props: ['msg'],
-          render(h, { props, children }) {
-            return h('div', null, [props.msg, ' '].concat(children))
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<div>foo bar</div>')
-    vm.test = 'qux'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<div>qux bar</div>')
-    }).then(done)
-  })
-
-  it('should expose all props when not declared', done => {
-    const fn = {
-      functional: true,
-      render(h, { props }) {
-        return h('div', `${props.msg} ${props.kebabMsg}`)
-      }
-    }
-
-    const vm = new Vue({
-      data: { test: 'foo' },
-      render(h) {
-        return h('div', [
-          h(fn, {
-            props: { msg: this.test },
-            attrs: { 'kebab-msg': 'bar' }
-          })
-        ])
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe('<div>foo bar</div>')
-    vm.test = 'qux'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<div>qux bar</div>')
-    }).then(done)
-  })
-
-  it('should expose data.on as listeners', () => {
-    const foo = vi.fn()
-    const bar = vi.fn()
-    const vm = new Vue({
-      template: '<div><wrap @click="foo" @test="bar"/></div>',
-      methods: { foo, bar },
-      components: {
-        wrap: {
-          functional: true,
-          render(h, { listeners }) {
-            return h('div', {
-              on: {
-                click: [listeners.click, () => listeners.test('bar')]
-              }
-            })
-          }
-        }
-      }
-    }).$mount()
-
-    document.body.appendChild(vm.$el)
-    triggerEvent(vm.$el.children[0], 'click')
-    expect(foo).toHaveBeenCalled()
-    expect(foo.mock.calls[0][0].type).toBe('click') // should have click event
-    triggerEvent(vm.$el.children[0], 'mousedown')
-    expect(bar).toHaveBeenCalledWith('bar')
-    document.body.removeChild(vm.$el)
-  })
-
-  it('should expose scopedSlots on render context', () => {
-    const vm = new Vue({
-      template:
-        '<div><wrap>foo<p slot="p" slot-scope="a">{{ a }}</p></wrap></div>',
-      components: {
-        wrap: {
-          functional: true,
-          render(h, { scopedSlots }) {
-            return [
-              // scoped
-              scopedSlots.p('a'),
-              // normal slot content should be exposed as well
-              scopedSlots.default()
-            ]
-          }
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('afoo')
-  })
-
-  it('should support returning more than one root node', () => {
-    const vm = new Vue({
-      template: `<div><test></test></div>`,
-      components: {
-        test: {
-          functional: true,
-          render(h) {
-            return [h('span', 'foo'), h('span', 'bar')]
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>foo</span><span>bar</span>')
-  })
-
-  it('should support slots', () => {
-    const vm = new Vue({
-      data: { test: 'foo' },
-      template:
-        '<div><wrap><div slot="a">foo</div><div slot="b">bar</div></wrap></div>',
-      components: {
-        wrap: {
-          functional: true,
-          props: ['msg'],
-          render(h, { slots }) {
-            slots = slots()
-            return h('div', null, [slots.b, slots.a])
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<div><div>bar</div><div>foo</div></div>')
-  })
-
-  it('should let vnode raw data pass through', done => {
-    const onValid = vi.fn()
-    const vm = new Vue({
-      data: { msg: 'hello' },
-      template: `<div>
-        <validate field="field1" @valid="onValid">
-          <input type="text" v-model="msg">
-        </validate>
-      </div>`,
-      components: {
-        validate: {
-          functional: true,
-          props: ['field'],
-          render(h, { props, children, data: { on } }) {
-            props.child = children[0]
-            return h('validate-control', { props, on })
-          }
-        },
-        'validate-control': {
-          props: ['field', 'child'],
-          render() {
-            return this.child
-          },
-          mounted() {
-            this.$el.addEventListener('input', this.onInput)
-          },
-          destroyed() {
-            this.$el.removeEventListener('input', this.onInput)
-          },
-          methods: {
-            onInput(e) {
-              const value = e.target.value
-              if (this.validate(value)) {
-                this.$emit('valid', this)
-              }
-            },
-            // something validation logic here
-            validate(val) {
-              return val.length > 0
-            }
-          }
-        }
-      },
-      methods: { onValid }
-    }).$mount()
-    document.body.appendChild(vm.$el)
-    const input = vm.$el.querySelector('input')
-    expect(onValid).not.toHaveBeenCalled()
-    waitForUpdate(() => {
-      input.value = 'foo'
-      triggerEvent(input, 'input')
-    })
-      .then(() => {
-        expect(onValid).toHaveBeenCalled()
-      })
-      .then(() => {
-        document.body.removeChild(vm.$el)
-        vm.$destroy()
-      })
-      .then(done)
-  })
-
-  it('create empty vnode when render return null', () => {
-    const child = {
-      functional: true,
-      render() {
-        return null
-      }
-    }
-    const vm = new Vue({
-      components: {
-        child
-      }
-    })
-    const h = vm.$createElement
-    const vnode = h('child')
-    expect(vnode).toEqual(createEmptyVNode())
-  })
-
-  // #7282
-  it('should normalize top-level arrays', () => {
-    const Foo = {
-      functional: true,
-      render(h) {
-        return [h('span', 'hi'), null]
-      }
-    }
-    const vm = new Vue({
-      template: `<div><foo/></div>`,
-      components: { Foo }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>hi</span>')
-  })
-
-  it('should work when used as named slot and returning array', () => {
-    const Foo = {
-      template: `<div><slot name="test"/></div>`
-    }
-
-    const Bar = {
-      functional: true,
-      render: h => [h('div', 'one'), h('div', 'two'), h(Baz)]
-    }
-
-    const Baz = {
-      functional: true,
-      render: h => h('div', 'three')
-    }
-
-    const vm = new Vue({
-      template: `<foo><bar slot="test"/></foo>`,
-      components: { Foo, Bar }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe(
-      '<div>one</div><div>two</div><div>three</div>'
-    )
-  })
-
-  it('should apply namespace when returning arrays', () => {
-    const Child = {
-      functional: true,
-      render: h => [h('foo'), h('bar')]
-    }
-    const vm = new Vue({
-      template: `<svg><child/></svg>`,
-      components: { Child }
-    }).$mount()
-
-    expect(vm.$el.childNodes[0].namespaceURI).toContain('svg')
-    expect(vm.$el.childNodes[1].namespaceURI).toContain('svg')
-  })
-
-  it('should work with render fns compiled from template', done => {
-    const render = function (_h, _vm) {
-      const _c = _vm._c
-      return _c(
-        'div',
-        [
-          _c('h2', { staticClass: 'red' }, [_vm._v(_vm._s(_vm.props.msg))]),
-          _vm._t('default'),
-          _vm._t('slot2'),
-          _vm._t('scoped', null, { msg: _vm.props.msg }),
-          _vm._m(0),
-          _c(
-            'div',
-            { staticClass: 'clickable', on: { click: _vm.parent.fn } },
-            [_vm._v('click me')]
-          )
-        ],
-        2
-      )
-    }
-    const staticRenderFns = [
-      function (_h, _vm) {
-        const _c = _vm._c
-        return _c('div', [_vm._v('Some '), _c('span', [_vm._v('text')])])
-      }
-    ]
-
-    const child = {
-      functional: true,
-      _compiled: true,
-      render,
-      staticRenderFns
-    }
-
-    const parent = new Vue({
-      components: {
-        child
-      },
-      data: {
-        msg: 'hello'
-      },
-      template: `
-      <div>
-        <child :msg="msg">
-          <span>{{ msg }}</span>
-          <div slot="slot2">Second slot</div>
-          <template slot="scoped" slot-scope="scope">{{ scope.msg }}</template>
-        </child>
-      </div>
-      `,
-      methods: {
-        fn() {
-          this.msg = 'bye'
-        }
-      }
-    }).$mount()
-
-    function assertMarkup() {
-      expect(parent.$el.innerHTML).toBe(
-        `<div>` +
-          `<h2 class="red">${parent.msg}</h2>` +
-          `<span>${parent.msg}</span> ` +
-          `<div>Second slot</div>` +
-          parent.msg +
-          // static
-          `<div>Some <span>text</span></div>` +
-          `<div class="clickable">click me</div>` +
-          `</div>`
-      )
-    }
-
-    assertMarkup()
-    triggerEvent(parent.$el.querySelector('.clickable'), 'click')
-    waitForUpdate(assertMarkup).then(done)
-  })
-
-  // #8468
-  it('should normalize nested arrays when use functional components with v-for', () => {
-    const Foo = {
-      functional: true,
-      props: {
-        name: {}
-      },
-      render(h, context) {
-        return [h('span', 'hi'), h('span', context.props.name)]
-      }
-    }
-    const vm = new Vue({
-      template: `<div><foo v-for="name in names" :name="name" /></div>`,
-      data: {
-        names: ['foo', 'bar']
-      },
-      components: { Foo }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe(
-      '<span>hi</span><span>foo</span><span>hi</span><span>bar</span>'
-    )
-  })
-})
diff --git a/test/unit/features/options/inheritAttrs.spec.ts b/test/unit/features/options/inheritAttrs.spec.ts
deleted file mode 100644
index 7ccc7c05eb0..00000000000
--- a/test/unit/features/options/inheritAttrs.spec.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import Vue from 'vue'
-
-describe('Options inheritAttrs', () => {
-  it('should work', done => {
-    const vm = new Vue({
-      template: `<foo :id="foo"/>`,
-      data: { foo: 'foo' },
-      components: {
-        foo: {
-          inheritAttrs: false,
-          template: `<div>foo</div>`
-        }
-      }
-    }).$mount()
-    expect(vm.$el.id).toBe('')
-    vm.foo = 'bar'
-    waitForUpdate(() => {
-      expect(vm.$el.id).toBe('')
-    }).then(done)
-  })
-
-  it('with inner v-bind', done => {
-    const vm = new Vue({
-      template: `<foo :id="foo"/>`,
-      data: { foo: 'foo' },
-      components: {
-        foo: {
-          inheritAttrs: false,
-          template: `<div><div v-bind="$attrs"></div></div>`
-        }
-      }
-    }).$mount()
-    expect(vm.$el.children[0].id).toBe('foo')
-    vm.foo = 'bar'
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].id).toBe('bar')
-    }).then(done)
-  })
-})
diff --git a/test/unit/features/options/inject.spec.ts b/test/unit/features/options/inject.spec.ts
deleted file mode 100644
index b4c295e3f51..00000000000
--- a/test/unit/features/options/inject.spec.ts
+++ /dev/null
@@ -1,723 +0,0 @@
-import Vue from 'vue'
-import { Observer } from 'core/observer/index'
-import { isNative, isObject, hasOwn, nextTick } from 'core/util/index'
-import testObjectOption from '../../../helpers/test-object-option'
-
-describe('Options provide/inject', () => {
-  testObjectOption('inject')
-
-  let injected
-  const injectedComp = {
-    inject: ['foo', 'bar'],
-    render() {},
-    created() {
-      injected = [this.foo, this.bar]
-    }
-  }
-
-  beforeEach(() => {
-    injected = null
-  })
-
-  it('should work', () => {
-    new Vue({
-      template: `<child/>`,
-      provide: {
-        foo: 1,
-        bar: false
-      },
-      components: {
-        child: {
-          template: `<injected-comp/>`,
-          components: {
-            injectedComp
-          }
-        }
-      }
-    }).$mount()
-
-    expect(injected).toEqual([1, false])
-  })
-
-  it('should use closest parent', () => {
-    new Vue({
-      template: `<child/>`,
-      provide: {
-        foo: 1,
-        bar: null
-      },
-      components: {
-        child: {
-          provide: {
-            foo: 3
-          },
-          template: `<injected-comp/>`,
-          components: {
-            injectedComp
-          }
-        }
-      }
-    }).$mount()
-
-    expect(injected).toEqual([3, null])
-  })
-
-  it('provide function', () => {
-    new Vue({
-      template: `<child/>`,
-      data: {
-        a: 1,
-        b: false
-      },
-      provide() {
-        return {
-          foo: this.a,
-          bar: this.b
-        }
-      },
-      components: {
-        child: {
-          template: `<injected-comp/>`,
-          components: {
-            injectedComp
-          }
-        }
-      }
-    }).$mount()
-
-    expect(injected).toEqual([1, false])
-  })
-
-  it('inject with alias', () => {
-    const injectAlias = {
-      inject: {
-        baz: 'foo',
-        qux: 'bar'
-      },
-      render() {},
-      created() {
-        injected = [this.baz, this.qux]
-      }
-    }
-
-    new Vue({
-      template: `<child/>`,
-      provide: {
-        foo: false,
-        bar: 2
-      },
-      components: {
-        child: {
-          template: `<inject-alias/>`,
-          components: {
-            injectAlias
-          }
-        }
-      }
-    }).$mount()
-
-    expect(injected).toEqual([false, 2])
-  })
-
-  it('inject before resolving data/props', () => {
-    const vm = new Vue({
-      provide: {
-        foo: 1
-      }
-    })
-
-    const child = new Vue({
-      parent: vm,
-      inject: ['foo'],
-      data() {
-        return {
-          bar: this.foo + 1
-        }
-      },
-      props: {
-        baz: {
-          default() {
-            return this.foo + 2
-          }
-        }
-      }
-    })
-
-    expect(child.foo).toBe(1)
-    expect(child.bar).toBe(2)
-    expect(child.baz).toBe(3)
-  })
-
-  // GitHub issue #5194
-  it('should work with functional', () => {
-    new Vue({
-      template: `<child/>`,
-      provide: {
-        foo: 1,
-        bar: false
-      },
-      components: {
-        child: {
-          functional: true,
-          inject: ['foo', 'bar'],
-          render(h, context) {
-            const { injections } = context
-            injected = [injections.foo, injections.bar]
-          }
-        }
-      }
-    }).$mount()
-
-    expect(injected).toEqual([1, false])
-  })
-
-  if (typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys)) {
-    it('with Symbol keys', () => {
-      const s = Symbol()
-      const vm = new Vue({
-        template: `<child/>`,
-        provide: {
-          [s]: 123
-        },
-        components: {
-          child: {
-            inject: { s },
-            template: `<div>{{ s }}</div>`
-          }
-        }
-      }).$mount()
-      expect(vm.$el.textContent).toBe('123')
-    })
-
-    it('should merge symbol provide from mixins (functions)', () => {
-      const keyA = Symbol('foo')
-      const keyB = Symbol('bar')
-
-      const mixinA = { provide: () => ({ [keyA]: 'foo' }) }
-      const mixinB = { provide: () => ({ [keyB]: 'bar' }) }
-      const child = {
-        inject: {
-          foo: keyA,
-          bar: keyB
-        },
-        template: `<span/>`,
-        created() {
-          injected = [this.foo, this.bar]
-        }
-      }
-      new Vue({
-        mixins: [mixinA, mixinB],
-        render(h) {
-          return h(child)
-        }
-      }).$mount()
-
-      expect(injected).toEqual(['foo', 'bar'])
-    })
-  }
-
-  // GitHub issue #5223
-  it('should work with reactive array', done => {
-    const vm = new Vue({
-      template: `<div><child></child></div>`,
-      data() {
-        return {
-          foo: []
-        }
-      },
-      provide() {
-        return {
-          foo: this.foo
-        }
-      },
-      components: {
-        child: {
-          inject: ['foo'],
-          template: `<span>{{foo.length}}</span>`
-        }
-      }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toEqual(`<span>0</span>`)
-    vm.foo.push(vm.foo.length)
-    vm.$nextTick(() => {
-      expect(vm.$el.innerHTML).toEqual(`<span>1</span>`)
-      vm.foo.pop()
-      vm.$nextTick(() => {
-        expect(vm.$el.innerHTML).toEqual(`<span>0</span>`)
-        done()
-      })
-    })
-  })
-
-  it('should extend properly', () => {
-    const parent = Vue.extend({
-      template: `<span/>`,
-      inject: ['foo']
-    })
-
-    const child = parent.extend({
-      template: `<span/>`,
-      inject: ['bar'],
-      created() {
-        injected = [this.foo, this.bar]
-      }
-    })
-
-    new Vue({
-      template: `<div><parent/><child/></div>`,
-      provide: {
-        foo: 1,
-        bar: false
-      },
-      components: {
-        parent,
-        child
-      }
-    }).$mount()
-
-    expect(injected).toEqual([1, false])
-  })
-
-  it('should merge from mixins properly (objects)', () => {
-    const mixinA = { inject: { foo: 'foo' } }
-    const mixinB = { inject: { bar: 'bar' } }
-    const child = {
-      mixins: [mixinA, mixinB],
-      template: `<span/>`,
-      created() {
-        injected = [this.foo, this.bar]
-      }
-    }
-    new Vue({
-      provide: { foo: 'foo', bar: 'bar', baz: 'baz' },
-      render(h) {
-        return h(child)
-      }
-    }).$mount()
-
-    expect(injected).toEqual(['foo', 'bar'])
-  })
-
-  it('should merge from mixins properly (arrays)', () => {
-    const mixinA = { inject: ['foo'] }
-    const mixinB = { inject: ['bar'] }
-    const child = {
-      mixins: [mixinA, mixinB],
-      inject: ['baz'],
-      template: `<span/>`,
-      created() {
-        injected = [this.foo, this.bar, this.baz]
-      }
-    }
-    new Vue({
-      provide: { foo: 'foo', bar: 'bar', baz: 'baz' },
-      render(h) {
-        return h(child)
-      }
-    }).$mount()
-
-    expect(injected).toEqual(['foo', 'bar', 'baz'])
-  })
-
-  it('should merge from mixins properly (mix of objects and arrays)', () => {
-    const mixinA = { inject: { foo: 'foo' } }
-    const mixinB = { inject: ['bar'] }
-    const child = {
-      mixins: [mixinA, mixinB],
-      inject: { qux: 'baz' },
-      template: `<span/>`,
-      created() {
-        injected = [this.foo, this.bar, this.qux]
-      }
-    }
-    new Vue({
-      provide: { foo: 'foo', bar: 'bar', baz: 'baz' },
-      render(h) {
-        return h(child)
-      }
-    }).$mount()
-
-    expect(injected).toEqual(['foo', 'bar', 'baz'])
-  })
-
-  it('should warn when injections has been modified', () => {
-    const key = 'foo'
-    const vm = new Vue({
-      provide: {
-        foo: 1
-      }
-    })
-
-    const child = new Vue({
-      parent: vm,
-      inject: ['foo']
-    })
-
-    expect(child.foo).toBe(1)
-    child.foo = 2
-    expect(
-      `Avoid mutating an injected value directly since the changes will be ` +
-        `overwritten whenever the provided component re-renders. ` +
-        `injection being mutated: "${key}"`
-    ).toHaveBeenWarned()
-  })
-
-  it('should warn when injections cannot be found', () => {
-    const vm = new Vue({})
-    new Vue({
-      parent: vm,
-      inject: ['foo', 'bar'],
-      created() {}
-    })
-    expect(`Injection "foo" not found`).toHaveBeenWarned()
-    expect(`Injection "bar" not found`).toHaveBeenWarned()
-  })
-
-  it('should not warn when injections can be found', () => {
-    const vm = new Vue({
-      provide: {
-        foo: 1,
-        bar: false,
-        baz: undefined
-      }
-    })
-    new Vue({
-      parent: vm,
-      inject: ['foo', 'bar', 'baz'],
-      created() {}
-    })
-    expect(`Injection "foo" not found`).not.toHaveBeenWarned()
-    expect(`Injection "bar" not found`).not.toHaveBeenWarned()
-    expect(`Injection "baz" not found`).not.toHaveBeenWarned()
-  })
-
-  it('should not warn when injection key which is not provided is not enumerable', () => {
-    const parent = new Vue({ provide: { foo: 1 } })
-    const inject = { foo: 'foo' }
-    Object.defineProperty(inject, '__ob__', {
-      enumerable: false,
-      value: '__ob__'
-    })
-    new Vue({ parent, inject })
-    expect(`Injection "__ob__" not found`).not.toHaveBeenWarned()
-  })
-
-  // Github issue #6097
-  it('should not warn when injections cannot be found but have default value', () => {
-    const vm = new Vue({})
-    new Vue({
-      parent: vm,
-      inject: {
-        foo: { default: 1 },
-        bar: { default: false },
-        baz: { default: undefined }
-      },
-      created() {
-        injected = [this.foo, this.bar, this.baz]
-      }
-    })
-    expect(injected).toEqual([1, false, undefined])
-  })
-
-  it('should support name alias and default together', () => {
-    const vm = new Vue({
-      provide: {
-        FOO: 2
-      }
-    })
-    new Vue({
-      parent: vm,
-      inject: {
-        foo: { from: 'FOO', default: 1 },
-        bar: { default: false },
-        baz: { default: undefined }
-      },
-      created() {
-        injected = [this.foo, this.bar, this.baz]
-      }
-    })
-    expect(injected).toEqual([2, false, undefined])
-  })
-
-  it('should use provided value even if inject has default', () => {
-    const vm = new Vue({
-      provide: {
-        foo: 1,
-        bar: false,
-        baz: undefined
-      }
-    })
-    new Vue({
-      parent: vm,
-      inject: {
-        foo: { default: 2 },
-        bar: { default: 2 },
-        baz: { default: 2 }
-      },
-      created() {
-        injected = [this.foo, this.bar, this.baz]
-      }
-    })
-    expect(injected).toEqual([1, false, undefined])
-  })
-
-  // Github issue #6008
-  it('should merge provide from mixins (objects)', () => {
-    const mixinA = { provide: { foo: 'foo' } }
-    const mixinB = { provide: { bar: 'bar' } }
-    const child = {
-      inject: ['foo', 'bar'],
-      template: `<span/>`,
-      created() {
-        injected = [this.foo, this.bar]
-      }
-    }
-    new Vue({
-      mixins: [mixinA, mixinB],
-      render(h) {
-        return h(child)
-      }
-    }).$mount()
-
-    expect(injected).toEqual(['foo', 'bar'])
-  })
-
-  it('should merge provide from mixins (functions)', () => {
-    const mixinA = { provide: () => ({ foo: 'foo' }) }
-    const mixinB = { provide: () => ({ bar: 'bar' }) }
-    const child = {
-      inject: ['foo', 'bar'],
-      template: `<span/>`,
-      created() {
-        injected = [this.foo, this.bar]
-      }
-    }
-    new Vue({
-      mixins: [mixinA, mixinB],
-      render(h) {
-        return h(child)
-      }
-    }).$mount()
-
-    expect(injected).toEqual(['foo', 'bar'])
-  })
-
-  it('should merge provide from mixins (mix of objects and functions)', () => {
-    const mixinA = { provide: { foo: 'foo' } }
-    const mixinB = { provide: () => ({ bar: 'bar' }) }
-    const mixinC = { provide: { baz: 'baz' } }
-    const mixinD = { provide: () => ({ bam: 'bam' }) }
-    const child = {
-      inject: ['foo', 'bar', 'baz', 'bam'],
-      template: `<span/>`,
-      created() {
-        injected = [this.foo, this.bar, this.baz, this.bam]
-      }
-    }
-    new Vue({
-      mixins: [mixinA, mixinB, mixinC, mixinD],
-      render(h) {
-        return h(child)
-      }
-    }).$mount()
-
-    expect(injected).toEqual(['foo', 'bar', 'baz', 'bam'])
-  })
-
-  it('should merge provide from mixins and override existing keys', () => {
-    const mixinA = { provide: { foo: 'foo' } }
-    const mixinB = { provide: { foo: 'bar' } }
-    const child = {
-      inject: ['foo'],
-      template: `<span/>`,
-      created() {
-        injected = [this.foo]
-      }
-    }
-    new Vue({
-      mixins: [mixinA, mixinB],
-      render(h) {
-        return h(child)
-      }
-    }).$mount()
-
-    expect(injected).toEqual(['bar'])
-  })
-
-  it('should merge provide when Vue.extend', () => {
-    const mixinA = { provide: () => ({ foo: 'foo' }) }
-    const child = {
-      inject: ['foo', 'bar'],
-      template: `<span/>`,
-      created() {
-        injected = [this.foo, this.bar]
-      }
-    }
-    const Ctor = Vue.extend({
-      mixins: [mixinA],
-      provide: { bar: 'bar' },
-      render(h) {
-        return h(child)
-      }
-    })
-
-    new Ctor().$mount()
-
-    expect(injected).toEqual(['foo', 'bar'])
-  })
-
-  // #5913
-  it('should keep the reactive with provide', () => {
-    function isObserver(obj) {
-      if (isObject(obj)) {
-        return hasOwn(obj, '__ob__') && obj.__ob__ instanceof Observer
-      }
-      return false
-    }
-
-    const vm = new Vue({
-      template: `<div><child ref='child'></child></div>`,
-      data() {
-        return {
-          foo: {},
-          $foo: {},
-          foo1: []
-        }
-      },
-      provide() {
-        return {
-          foo: this.foo,
-          $foo: this.$foo,
-          foo1: this.foo1,
-          bar: {},
-          baz: []
-        }
-      },
-      components: {
-        child: {
-          inject: ['foo', '$foo', 'foo1', 'bar', 'baz'],
-          template: `<span/>`
-        }
-      }
-    }).$mount()
-    const child = vm.$refs.child
-    expect(isObserver(child.foo)).toBe(true)
-    expect(isObserver(child.$foo)).toBe(false)
-    expect(isObserver(child.foo1)).toBe(true)
-    expect(isObserver(child.bar)).toBe(false)
-    expect(isObserver(child.baz)).toBe(false)
-  })
-
-  // #6175
-  it('merge provide properly from mixins', () => {
-    const ProvideFooMixin = {
-      provide: {
-        foo: 'foo injected'
-      }
-    }
-
-    const ProvideBarMixin = {
-      provide: {
-        bar: 'bar injected'
-      }
-    }
-
-    const Child = {
-      inject: ['foo', 'bar'],
-      render(h) {
-        return h('div', [`foo: ${this.foo}, `, `bar: ${this.bar}`])
-      }
-    }
-
-    const Parent = {
-      mixins: [ProvideFooMixin, ProvideBarMixin],
-      render(h) {
-        return h(Child)
-      }
-    }
-
-    const vm = new Vue({
-      render(h) {
-        return h(Parent)
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe(`foo: foo injected, bar: bar injected`)
-  })
-
-  it('merge provide with object syntax when using Vue.extend', () => {
-    const child = {
-      inject: ['foo'],
-      template: `<span/>`,
-      created() {
-        injected = this.foo
-      }
-    }
-    const Ctor = Vue.extend({
-      provide: { foo: 'foo' },
-      render(h) {
-        return h(child)
-      }
-    })
-
-    new Ctor().$mount()
-
-    expect(injected).toEqual('foo')
-  })
-
-  // #7284
-  it('should not inject prototype properties', () => {
-    const vm = new Vue({
-      provide: {}
-    })
-    new Vue({
-      parent: vm,
-      inject: ['constructor']
-    })
-    expect(`Injection "constructor" not found`).toHaveBeenWarned()
-  })
-
-  // #12667
-  test('provide with getters', async () => {
-    const spy = vi.fn()
-    const Child = {
-      render() {},
-      inject: ['foo'],
-      mounted() {
-        spy(this.foo)
-      }
-    }
-
-    let val = 1
-    const vm = new Vue({
-      components: { Child },
-      template: `<Child v-if="ok" />`,
-      data() {
-        return {
-          ok: false
-        }
-      },
-      provide() {
-        return {
-          get foo() {
-            return val
-          }
-        }
-      }
-    }).$mount()
-
-    val = 2
-    vm.ok = true
-    await nextTick()
-    expect(spy).toHaveBeenCalledWith(2)
-  })
-
-  // #12854
-  test('should not mutate original provide options', () => {
-    const hairMixin = { provide: { hair: 'red' } }
-    const eyesMixin = { provide: { eyes: 'brown' } }
-    new Vue({ mixins: [hairMixin, eyesMixin], render() {} }).$mount()
-    expect(eyesMixin.provide).toStrictEqual({ eyes: 'brown' })
-  })
-})
diff --git a/test/unit/features/options/lifecycle.spec.ts b/test/unit/features/options/lifecycle.spec.ts
deleted file mode 100644
index 39e43881250..00000000000
--- a/test/unit/features/options/lifecycle.spec.ts
+++ /dev/null
@@ -1,338 +0,0 @@
-import Vue from 'vue'
-
-describe('Options lifecycle hooks', () => {
-  let spy
-  beforeEach(() => {
-    spy = vi.fn()
-  })
-
-  describe('beforeCreate', () => {
-    it('should allow modifying options', () => {
-      const vm = new Vue({
-        data: {
-          a: 1
-        },
-        beforeCreate() {
-          spy()
-          expect(this.a).toBeUndefined()
-          this.$options.computed = {
-            b() {
-              return this.a + 1
-            }
-          }
-        }
-      })
-      expect(spy).toHaveBeenCalled()
-      expect(vm.b).toBe(2)
-    })
-  })
-
-  describe('created', () => {
-    it('should have completed observation', () => {
-      new Vue({
-        data: {
-          a: 1
-        },
-        created() {
-          expect(this.a).toBe(1)
-          spy()
-        }
-      })
-      expect(spy).toHaveBeenCalled()
-    })
-  })
-
-  describe('beforeMount', () => {
-    it('should not have mounted', () => {
-      const vm = new Vue({
-        render() {},
-        beforeMount() {
-          spy()
-          expect(this._isMounted).toBe(false)
-          expect(this.$el).toBeUndefined() // due to empty mount
-          expect(this._vnode).toBeNull()
-          expect(this._watcher).toBeNull()
-        }
-      })
-      expect(spy).not.toHaveBeenCalled()
-      vm.$mount()
-      expect(spy).toHaveBeenCalled()
-    })
-  })
-
-  describe('mounted', () => {
-    it('should have mounted', () => {
-      const vm = new Vue({
-        template: '<div></div>',
-        mounted() {
-          spy()
-          expect(this._isMounted).toBe(true)
-          expect(this.$el.tagName).toBe('DIV')
-          expect(this._vnode.tag).toBe('div')
-        }
-      })
-      expect(spy).not.toHaveBeenCalled()
-      vm.$mount()
-      expect(spy).toHaveBeenCalled()
-    })
-
-    // #3898
-    it('should call for manually mounted instance with parent', () => {
-      const parent = new Vue()
-      expect(spy).not.toHaveBeenCalled()
-      new Vue({
-        parent,
-        template: '<div></div>',
-        mounted() {
-          spy()
-        }
-      }).$mount()
-      expect(spy).toHaveBeenCalled()
-    })
-
-    it('should mount child parent in correct order', () => {
-      const calls: any[] = []
-      new Vue({
-        template: '<div><test></test></div>',
-        mounted() {
-          calls.push('parent')
-        },
-        components: {
-          test: {
-            template: '<nested></nested>',
-            mounted() {
-              expect(this.$el.parentNode).toBeTruthy()
-              calls.push('child')
-            },
-            components: {
-              nested: {
-                template: '<div></div>',
-                mounted() {
-                  expect(this.$el.parentNode).toBeTruthy()
-                  calls.push('nested')
-                }
-              }
-            }
-          }
-        }
-      }).$mount()
-      expect(calls).toEqual(['nested', 'child', 'parent'])
-    })
-  })
-
-  describe('beforeUpdate', () => {
-    it('should be called before update', done => {
-      const vm = new Vue({
-        template: '<div>{{ msg }}</div>',
-        data: { msg: 'foo' },
-        beforeUpdate() {
-          spy()
-          expect(this.$el.textContent).toBe('foo')
-        }
-      }).$mount()
-      expect(spy).not.toHaveBeenCalled()
-      vm.msg = 'bar'
-      expect(spy).not.toHaveBeenCalled() // should be async
-      waitForUpdate(() => {
-        expect(spy).toHaveBeenCalled()
-      }).then(done)
-    })
-
-    it('should be called before render and allow mutating state', done => {
-      const vm = new Vue({
-        template: '<div>{{ msg }}</div>',
-        data: { msg: 'foo' },
-        beforeUpdate() {
-          this.msg += '!'
-        }
-      }).$mount()
-      expect(vm.$el.textContent).toBe('foo')
-      vm.msg = 'bar'
-      waitForUpdate(() => {
-        expect(vm.$el.textContent).toBe('bar!')
-      }).then(done)
-    })
-
-    // #8076
-    it('should not be called after destroy', done => {
-      const beforeUpdate = vi.fn()
-      const destroyed = vi.fn()
-
-      Vue.component('todo', {
-        template: '<div>{{todo.done}}</div>',
-        props: ['todo'],
-        destroyed,
-        beforeUpdate
-      })
-
-      const vm = new Vue({
-        template: `
-          <div>
-            <todo v-for="t in pendingTodos" :todo="t" :key="t.id"></todo>
-          </div>
-        `,
-        data() {
-          return {
-            todos: [{ id: 1, done: false }]
-          }
-        },
-        computed: {
-          pendingTodos() {
-            return this.todos.filter(t => !t.done)
-          }
-        }
-      }).$mount()
-
-      vm.todos[0].done = true
-      waitForUpdate(() => {
-        expect(destroyed).toHaveBeenCalled()
-        expect(beforeUpdate).not.toHaveBeenCalled()
-      }).then(done)
-    })
-  })
-
-  describe('updated', () => {
-    it('should be called after update', done => {
-      const vm = new Vue({
-        template: '<div>{{ msg }}</div>',
-        data: { msg: 'foo' },
-        updated() {
-          spy()
-          expect(this.$el.textContent).toBe('bar')
-        }
-      }).$mount()
-      expect(spy).not.toHaveBeenCalled()
-      vm.msg = 'bar'
-      expect(spy).not.toHaveBeenCalled() // should be async
-      waitForUpdate(() => {
-        expect(spy).toHaveBeenCalled()
-      }).then(done)
-    })
-
-    it('should be called after children are updated', done => {
-      const calls: any[] = []
-      const vm = new Vue({
-        template: '<div><test ref="child">{{ msg }}</test></div>',
-        data: { msg: 'foo' },
-        components: {
-          test: {
-            template: `<div><slot></slot></div>`,
-            updated() {
-              expect(this.$el.textContent).toBe('bar')
-              calls.push('child')
-            }
-          }
-        },
-        updated() {
-          expect(this.$el.textContent).toBe('bar')
-          calls.push('parent')
-        }
-      }).$mount()
-
-      expect(calls).toEqual([])
-      vm.msg = 'bar'
-      expect(calls).toEqual([])
-      waitForUpdate(() => {
-        expect(calls).toEqual(['child', 'parent'])
-      }).then(done)
-    })
-
-    // #8076
-    it('should not be called after destroy', done => {
-      const updated = vi.fn()
-      const destroyed = vi.fn()
-
-      Vue.component('todo', {
-        template: '<div>{{todo.done}}</div>',
-        props: ['todo'],
-        destroyed,
-        updated
-      })
-
-      const vm = new Vue({
-        template: `
-          <div>
-            <todo v-for="t in pendingTodos" :todo="t" :key="t.id"></todo>
-          </div>
-        `,
-        data() {
-          return {
-            todos: [{ id: 1, done: false }]
-          }
-        },
-        computed: {
-          pendingTodos() {
-            return this.todos.filter(t => !t.done)
-          }
-        }
-      }).$mount()
-
-      vm.todos[0].done = true
-      waitForUpdate(() => {
-        expect(destroyed).toHaveBeenCalled()
-        expect(updated).not.toHaveBeenCalled()
-      }).then(done)
-    })
-  })
-
-  describe('beforeDestroy', () => {
-    it('should be called before destroy', () => {
-      const vm = new Vue({
-        render() {},
-        beforeDestroy() {
-          spy()
-          expect(this._isBeingDestroyed).toBe(false)
-          expect(this._isDestroyed).toBe(false)
-        }
-      }).$mount()
-      expect(spy).not.toHaveBeenCalled()
-      vm.$destroy()
-      vm.$destroy()
-      expect(spy).toHaveBeenCalled()
-      expect(spy.mock.calls.length).toBe(1)
-    })
-  })
-
-  describe('destroyed', () => {
-    it('should be called after destroy', () => {
-      const vm = new Vue({
-        render() {},
-        destroyed() {
-          spy()
-          expect(this._isBeingDestroyed).toBe(true)
-          expect(this._isDestroyed).toBe(true)
-        }
-      }).$mount()
-      expect(spy).not.toHaveBeenCalled()
-      vm.$destroy()
-      vm.$destroy()
-      expect(spy).toHaveBeenCalled()
-      expect(spy.mock.calls.length).toBe(1)
-    })
-  })
-
-  it('should emit hook events', () => {
-    const created = vi.fn()
-    const mounted = vi.fn()
-    const destroyed = vi.fn()
-    const vm = new Vue({
-      render() {},
-      beforeCreate() {
-        this.$on('hook:created', created)
-        this.$on('hook:mounted', mounted)
-        this.$on('hook:destroyed', destroyed)
-      }
-    })
-
-    expect(created).toHaveBeenCalled()
-    expect(mounted).not.toHaveBeenCalled()
-    expect(destroyed).not.toHaveBeenCalled()
-
-    vm.$mount()
-    expect(mounted).toHaveBeenCalled()
-    expect(destroyed).not.toHaveBeenCalled()
-
-    vm.$destroy()
-    expect(destroyed).toHaveBeenCalled()
-  })
-})
diff --git a/test/unit/features/options/methods.spec.ts b/test/unit/features/options/methods.spec.ts
deleted file mode 100644
index 93f31f8ef4e..00000000000
--- a/test/unit/features/options/methods.spec.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import Vue from 'vue'
-import testObjectOption from '../../../helpers/test-object-option'
-
-describe('Options methods', () => {
-  testObjectOption('methods')
-
-  it('should have correct context', () => {
-    const vm = new Vue({
-      data: {
-        a: 1
-      },
-      methods: {
-        plus() {
-          this.a++
-        }
-      }
-    })
-    vm.plus()
-    expect(vm.a).toBe(2)
-  })
-
-  it('should warn methods of not function type', () => {
-    new Vue({
-      methods: {
-        hello: {}
-      }
-    })
-    expect(
-      'Method "hello" has type "object" in the component definition'
-    ).toHaveBeenWarned()
-  })
-
-  it('should warn methods conflicting with data', () => {
-    new Vue({
-      data: {
-        foo: 1
-      },
-      methods: {
-        foo() {}
-      }
-    })
-    expect(
-      `Method "foo" has already been defined as a data property`
-    ).toHaveBeenWarned()
-  })
-
-  it('should warn methods conflicting with internal methods', () => {
-    new Vue({
-      methods: {
-        _update() {}
-      }
-    })
-    expect(
-      `Method "_update" conflicts with an existing Vue instance method`
-    ).toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/options/mixins.spec.ts b/test/unit/features/options/mixins.spec.ts
deleted file mode 100644
index 760e07429b2..00000000000
--- a/test/unit/features/options/mixins.spec.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-import Vue from 'vue'
-import { mergeOptions } from 'core/util/index'
-
-describe('Options mixins', () => {
-  it('vm should have options from mixin', () => {
-    const mixin = {
-      directives: {
-        c: {}
-      },
-      methods: {
-        a: function () {}
-      }
-    }
-
-    const vm = new Vue({
-      mixins: [mixin],
-      methods: {
-        b: function () {}
-      }
-    })
-
-    expect(vm.a).toBeDefined()
-    expect(vm.b).toBeDefined()
-    expect(vm.$options.directives.c).toBeDefined()
-  })
-
-  it('should call hooks from mixins first', () => {
-    const a = {}
-    const b = {}
-    const c = {}
-    const f1 = function () {}
-    const f2 = function () {}
-    const f3 = function () {}
-    const mixinA = {
-      a: 1,
-      template: 'foo',
-      directives: {
-        a: a
-      },
-      created: f1
-    }
-    const mixinB = {
-      b: 1,
-      directives: {
-        b: b
-      },
-      created: f2
-    }
-    const result = mergeOptions(
-      {},
-      {
-        directives: {
-          c: c
-        },
-        template: 'bar',
-        mixins: [mixinA, mixinB],
-        created: f3
-      }
-    )
-    expect(result.a).toBe(1)
-    expect(result.b).toBe(1)
-    expect(result.directives?.a).toBe(a)
-    expect(result.directives?.b).toBe(b)
-    expect(result.directives?.c).toBe(c)
-    expect(result.created?.[0]).toBe(f1)
-    expect(result.created?.[1]).toBe(f2)
-    expect(result.created?.[2]).toBe(f3)
-    expect(result.template).toBe('bar')
-  })
-
-  it('mixin methods should not override defined method', () => {
-    const f1 = function () {}
-    const f2 = function () {}
-    const f3 = function () {}
-    const mixinA = {
-      methods: {
-        xyz: f1
-      }
-    }
-    const mixinB = {
-      methods: {
-        xyz: f2
-      }
-    }
-    const result = mergeOptions(
-      {},
-      {
-        mixins: [mixinA, mixinB],
-        methods: {
-          xyz: f3
-        }
-      }
-    )
-    expect(result.methods?.xyz).toBe(f3)
-  })
-
-  it('should accept constructors as mixins', () => {
-    const mixin = Vue.extend({
-      directives: {
-        c: {}
-      },
-      methods: {
-        a: function () {}
-      }
-    })
-
-    const vm = new Vue({
-      mixins: [mixin],
-      methods: {
-        b: function () {}
-      }
-    })
-
-    expect(vm.a).toBeDefined()
-    expect(vm.b).toBeDefined()
-    expect(vm.$options.directives.c).toBeDefined()
-  })
-
-  it('should accept further extended constructors as mixins', () => {
-    const spy1 = vi.fn()
-    const spy2 = vi.fn()
-
-    const mixinA = Vue.extend({
-      created: spy1,
-      directives: {
-        c: {}
-      },
-      methods: {
-        a: function () {}
-      }
-    })
-
-    const mixinB = mixinA.extend({
-      created: spy2
-    })
-
-    const vm = new Vue({
-      mixins: [mixinB],
-      methods: {
-        b: function () {}
-      }
-    })
-
-    expect(spy1).toHaveBeenCalledTimes(1)
-    expect(spy2).toHaveBeenCalledTimes(1)
-    expect(vm.a).toBeDefined()
-    expect(vm.b).toBeDefined()
-    expect(vm.$options.directives.c).toBeDefined()
-  })
-})
diff --git a/test/unit/features/options/name.spec.ts b/test/unit/features/options/name.spec.ts
deleted file mode 100644
index 753acf26a47..00000000000
--- a/test/unit/features/options/name.spec.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import Vue from 'vue'
-
-describe('Options name', () => {
-  it('should contain itself in self components', () => {
-    const vm = Vue.extend({
-      name: 'SuperVue'
-    })
-
-    expect(vm.options.components['SuperVue']).toEqual(vm)
-  })
-
-  it('should warn when incorrect name given', () => {
-    Vue.extend({
-      name: 'Hyper*Vue'
-    })
-
-    /* eslint-disable */
-    expect(`Invalid component name: "Hyper*Vue".`).toHaveBeenWarned()
-    /* eslint-enable */
-
-    Vue.extend({
-      name: '2Cool2BValid'
-    })
-
-    /* eslint-disable */
-    expect(`Invalid component name: "2Cool2BValid".`).toHaveBeenWarned()
-    /* eslint-enable */
-  })
-
-  it('id should not override given name when using Vue.component', () => {
-    const SuperComponent = Vue.component('super-component', {
-      name: 'SuperVue'
-    })!
-
-    expect(SuperComponent.options.components['SuperVue']).toEqual(
-      SuperComponent
-    )
-    expect(SuperComponent.options.components['super-component']).toEqual(
-      SuperComponent
-    )
-  })
-
-  it('should allow all potential custom element name for component name including non-alphanumeric characters', () => {
-    Vue.extend({
-      name: 'my-컴포넌트'
-    })
-
-    expect(`Invalid component name`).not.toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/options/parent.spec.ts b/test/unit/features/options/parent.spec.ts
deleted file mode 100644
index 6defc783eac..00000000000
--- a/test/unit/features/options/parent.spec.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import Vue from 'vue'
-
-describe('Options parent', () => {
-  it('should work', () => {
-    const parent = new Vue({
-      render() {}
-    }).$mount()
-
-    const child = new Vue({
-      parent: parent,
-      render() {}
-    }).$mount()
-
-    // this option is straight-forward
-    // it should register 'parent' as a $parent for 'child'
-    // and push 'child' to $children array on 'parent'
-    expect(child.$options.parent).toBeDefined()
-    expect(child.$options.parent).toEqual(parent)
-    expect(child.$parent).toBeDefined()
-    expect(child.$parent).toEqual(parent)
-    expect(parent.$children).toContain(child)
-
-    // destroy 'child' and check if it was removed from 'parent' $children
-    child.$destroy()
-    expect(parent.$children.length).toEqual(0)
-    parent.$destroy()
-  })
-})
diff --git a/test/unit/features/options/props.spec.ts b/test/unit/features/options/props.spec.ts
deleted file mode 100644
index 23f30df429b..00000000000
--- a/test/unit/features/options/props.spec.ts
+++ /dev/null
@@ -1,614 +0,0 @@
-import Vue from 'vue'
-import { hasSymbol } from 'core/util/env'
-import testObjectOption from '../../../helpers/test-object-option'
-import { ref } from 'v3'
-
-describe('Options props', () => {
-  testObjectOption('props')
-
-  it('array syntax', done => {
-    const vm = new Vue({
-      data: {
-        b: 'bar'
-      },
-      template: '<test v-bind:b="b" ref="child"></test>',
-      components: {
-        test: {
-          props: ['b'],
-          template: '<div>{{b}}</div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('bar')
-    vm.b = 'baz'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('baz')
-      vm.$refs.child.b = 'qux'
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('qux')
-        expect('Avoid mutating a prop directly').toHaveBeenWarned()
-      })
-      .then(done)
-  })
-
-  it('object syntax', done => {
-    const vm = new Vue({
-      data: {
-        b: 'bar'
-      },
-      template: '<test v-bind:b="b" ref="child"></test>',
-      components: {
-        test: {
-          props: { b: String },
-          template: '<div>{{b}}</div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('bar')
-    vm.b = 'baz'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('baz')
-      vm.$refs.child.b = 'qux'
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('qux')
-        expect('Avoid mutating a prop directly').toHaveBeenWarned()
-      })
-      .then(done)
-  })
-
-  it('warn mixed syntax', () => {
-    new Vue({
-      props: [{ b: String }]
-    })
-    expect('props must be strings when using array syntax').toHaveBeenWarned()
-  })
-
-  it('default values', () => {
-    const vm = new Vue({
-      data: {
-        b: undefined
-      },
-      template: '<test :b="b"></test>',
-      components: {
-        test: {
-          props: {
-            a: {
-              default: 'A' // absent
-            },
-            b: {
-              default: 'B' // undefined
-            }
-          },
-          template: '<div>{{a}}{{b}}</div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('AB')
-  })
-
-  it('default value reactivity', done => {
-    const vm = new Vue({
-      props: {
-        a: {
-          default: () => ({ b: 1 })
-        }
-      },
-      propsData: {
-        a: undefined
-      },
-      template: '<div>{{ a.b }}</div>'
-    }).$mount()
-    expect(vm.$el.textContent).toBe('1')
-    vm.a.b = 2
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('2')
-    }).then(done)
-  })
-
-  it('default value Function', () => {
-    const func = () => 132
-    const vm = new Vue({
-      props: {
-        a: {
-          type: Function,
-          default: func
-        }
-      },
-      propsData: {
-        a: undefined
-      }
-    })
-    expect(vm.a).toBe(func)
-  })
-
-  it('warn object/array default values', () => {
-    new Vue({
-      props: {
-        a: {
-          default: { b: 1 }
-        }
-      },
-      propsData: {
-        a: undefined
-      }
-    })
-    expect(
-      'Props with type Object/Array must use a factory function'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn missing required', () => {
-    new Vue({
-      template: '<test></test>',
-      components: {
-        test: {
-          props: { a: { required: true } },
-          template: '<div>{{a}}</div>'
-        }
-      }
-    }).$mount()
-    expect('Missing required prop: "a"').toHaveBeenWarned()
-  })
-
-  describe('assertions', () => {
-    function makeInstance(value, type, validator?, required?) {
-      return new Vue({
-        template: '<test :test="val"></test>',
-        data: {
-          val: value
-        },
-        components: {
-          test: {
-            template: '<div></div>',
-            props: {
-              test: {
-                type,
-                validator,
-                required
-              }
-            }
-          }
-        }
-      }).$mount()
-    }
-
-    it('string', () => {
-      makeInstance('hello', String)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(123, String)
-      expect(
-        'Expected String with value "123", got Number with value 123'
-      ).toHaveBeenWarned()
-    })
-
-    it('number', () => {
-      makeInstance(123, Number)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance('123', Number)
-      expect(
-        'Expected Number with value 123, got String with value "123"'
-      ).toHaveBeenWarned()
-    })
-
-    it('number & boolean', () => {
-      makeInstance(123, Number)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(false, Number)
-      expect('Expected Number, got Boolean with value false').toHaveBeenWarned()
-    })
-
-    it('string & boolean', () => {
-      makeInstance('hello', String)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(true, String)
-      expect('Expected String, got Boolean with value true').toHaveBeenWarned()
-    })
-
-    it('boolean', () => {
-      makeInstance(true, Boolean)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance('123', Boolean)
-      expect('Expected Boolean, got String with value "123"').toHaveBeenWarned()
-    })
-
-    it('function', () => {
-      makeInstance(() => {}, Function)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(123, Function)
-      expect('Expected Function, got Number with value 123').toHaveBeenWarned()
-    })
-
-    it('object', () => {
-      makeInstance({}, Object)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance([], Object)
-      expect('Expected Object, got Array').toHaveBeenWarned()
-    })
-
-    it('array', () => {
-      makeInstance([], Array)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance({}, Array)
-      expect('Expected Array, got Object').toHaveBeenWarned()
-    })
-
-    it('primitive wrapper objects', () => {
-      /* eslint-disable no-new-wrappers */
-      makeInstance(new String('s'), String)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(new Number(1), Number)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(new Boolean(true), Boolean)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      /* eslint-enable no-new-wrappers */
-    })
-
-    if (hasSymbol) {
-      it('symbol', () => {
-        makeInstance(Symbol('foo'), Symbol)
-        expect((console.error as any).mock.calls.length).toBe(0)
-        makeInstance({}, Symbol)
-        expect('Expected Symbol, got Object').toHaveBeenWarned()
-      })
-
-      it('warns when expected an explicable type but Symbol was provided', () => {
-        makeInstance(Symbol('foo'), String)
-        expect('Expected String, got Symbol').toHaveBeenWarned()
-      })
-
-      it('warns when expected an explicable type but Symbol was provided', () => {
-        makeInstance(Symbol('foo'), [String, Number])
-        expect('Expected String, Number, got Symbol').toHaveBeenWarned()
-      })
-    }
-
-    if (typeof BigInt !== 'undefined') {
-      /* global BigInt */
-      it('bigint', () => {
-        makeInstance(BigInt(100), BigInt)
-        expect((console.error as any).mock.calls.length).toBe(0)
-        makeInstance({}, BigInt)
-        expect('Expected BigInt, got Object').toHaveBeenWarned()
-      })
-    }
-
-    it('custom constructor', () => {
-      function Class() {}
-      makeInstance(new Class(), Class)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance({}, Class)
-      expect('type check failed').toHaveBeenWarned()
-    })
-
-    it('multiple types', () => {
-      makeInstance([], [Array, Number, Boolean])
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance({}, [Array, Number, Boolean])
-      expect('Expected Array, Number, Boolean, got Object').toHaveBeenWarned()
-    })
-
-    it('custom validator', () => {
-      makeInstance(123, null, v => v === 123)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(123, null, v => v === 234)
-      expect('custom validator check failed').toHaveBeenWarned()
-    })
-
-    it('type check + custom validator', () => {
-      makeInstance(123, Number, v => v === 123)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(123, Number, v => v === 234)
-      expect('custom validator check failed').toHaveBeenWarned()
-      makeInstance(123, String, v => v === 123)
-      expect(
-        'Expected String with value "123", got Number with value 123'
-      ).toHaveBeenWarned()
-    })
-
-    it('multiple types + custom validator', () => {
-      makeInstance(123, [Number, String, Boolean], v => v === 123)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(123, [Number, String, Boolean], v => v === 234)
-      expect('custom validator check failed').toHaveBeenWarned()
-      makeInstance(123, [String, Boolean], v => v === 123)
-      expect('Expected String, Boolean').toHaveBeenWarned()
-    })
-
-    it('optional with type + null/undefined', () => {
-      makeInstance(undefined, String)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(null, String)
-      expect((console.error as any).mock.calls.length).toBe(0)
-    })
-
-    it('required with type + null/undefined', () => {
-      makeInstance(undefined, String, null, true)
-      expect((console.error as any).mock.calls.length).toBe(1)
-      expect('Expected String').toHaveBeenWarned()
-      makeInstance(null, Boolean, null, true)
-      expect((console.error as any).mock.calls.length).toBe(2)
-      expect('Expected Boolean').toHaveBeenWarned()
-    })
-
-    it('optional prop of any type (type: true or prop: true)', () => {
-      makeInstance(1, true)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance('any', true)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance({}, true)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(undefined, true)
-      expect((console.error as any).mock.calls.length).toBe(0)
-      makeInstance(null, true)
-      expect((console.error as any).mock.calls.length).toBe(0)
-    })
-  })
-
-  it('should work with v-bind', () => {
-    const vm = new Vue({
-      template: `<test v-bind="{ a: 1, b: 2 }"></test>`,
-      components: {
-        test: {
-          props: ['a', 'b'],
-          template: '<div>{{ a }} {{ b }}</div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('1 2')
-  })
-
-  it('should warn data fields already defined as a prop', () => {
-    new Vue({
-      template: '<test a="1"></test>',
-      components: {
-        test: {
-          template: '<div></div>',
-          data: function () {
-            return { a: 123 }
-          },
-          props: {
-            a: null
-          }
-        }
-      }
-    }).$mount()
-    expect('already declared as a prop').toHaveBeenWarned()
-  })
-
-  it('should warn methods already defined as a prop', () => {
-    new Vue({
-      template: '<test a="1"></test>',
-      components: {
-        test: {
-          template: '<div></div>',
-          props: {
-            a: null
-          },
-          methods: {
-            a() {}
-          }
-        }
-      }
-    }).$mount()
-    expect(`Method "a" has already been defined as a prop`).toHaveBeenWarned()
-    expect(`Avoid mutating a prop directly`).toHaveBeenWarned()
-  })
-
-  it('treat boolean props properly', () => {
-    const vm = new Vue({
-      template: '<comp ref="child" prop-a prop-b="prop-b"></comp>',
-      components: {
-        comp: {
-          template: '<div></div>',
-          props: {
-            propA: Boolean,
-            propB: Boolean,
-            propC: Boolean
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$refs.child.propA).toBe(true)
-    expect(vm.$refs.child.propB).toBe(true)
-    expect(vm.$refs.child.propC).toBe(false)
-  })
-
-  it('should respect default value of a Boolean prop', function () {
-    const vm = new Vue({
-      template: '<test></test>',
-      components: {
-        test: {
-          props: {
-            prop: {
-              type: Boolean,
-              default: true
-            }
-          },
-          template: '<div>{{prop}}</div>'
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('true')
-  })
-
-  it('non reactive values passed down as prop should not be converted', done => {
-    const a = Object.freeze({
-      nested: {
-        msg: 'hello'
-      }
-    })
-    const parent = new Vue({
-      template: '<comp :a="a.nested"></comp>',
-      data: {
-        a: a
-      },
-      components: {
-        comp: {
-          template: '<div></div>',
-          props: ['a']
-        }
-      }
-    }).$mount()
-    const child = parent.$children[0]
-    expect(child.a.msg).toBe('hello')
-    expect(child.a.__ob__).toBeUndefined() // should not be converted
-    parent.a = Object.freeze({
-      nested: {
-        msg: 'yo'
-      }
-    })
-    waitForUpdate(() => {
-      expect(child.a.msg).toBe('yo')
-      expect(child.a.__ob__).toBeUndefined()
-    }).then(done)
-  })
-
-  it('should not warn for non-required, absent prop', function () {
-    new Vue({
-      template: '<test></test>',
-      components: {
-        test: {
-          template: '<div></div>',
-          props: {
-            prop: {
-              type: String
-            }
-          }
-        }
-      }
-    }).$mount()
-    expect((console.error as any).mock.calls.length).toBe(0)
-  })
-
-  // #3453
-  it('should not fire watcher on object/array props when parent re-renders', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        arr: []
-      },
-      template: '<test :prop="arr">hi</test>',
-      components: {
-        test: {
-          props: ['prop'],
-          watch: {
-            prop: spy
-          },
-          template: '<div><slot></slot></div>'
-        }
-      }
-    }).$mount()
-    vm.$forceUpdate()
-    waitForUpdate(() => {
-      expect(spy).not.toHaveBeenCalled()
-    }).then(done)
-  })
-
-  // #4090
-  it('should not trigger watcher on default value', done => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      template: `<test :value="a" :test="b"></test>`,
-      data: {
-        a: 1,
-        b: undefined
-      },
-      components: {
-        test: {
-          template: '<div>{{ value }}</div>',
-          props: {
-            value: { type: Number },
-            test: {
-              type: Object,
-              default: () => ({})
-            }
-          },
-          watch: {
-            test: spy
-          }
-        }
-      }
-    }).$mount()
-
-    vm.a++
-    waitForUpdate(() => {
-      expect(spy).not.toHaveBeenCalled()
-      vm.b = {}
-    })
-      .then(() => {
-        expect(spy.mock.calls.length).toBe(1)
-      })
-      .then(() => {
-        vm.b = undefined
-      })
-      .then(() => {
-        expect(spy.mock.calls.length).toBe(2)
-        vm.a++
-      })
-      .then(() => {
-        expect(spy.mock.calls.length).toBe(2)
-      })
-      .then(done)
-  })
-
-  it('warn reserved props', () => {
-    const specialAttrs = ['key', 'ref', 'slot', 'is', 'slot-scope']
-    new Vue({
-      props: specialAttrs
-    })
-    specialAttrs.forEach(attr => {
-      expect(`"${attr}" is a reserved attribute`).toHaveBeenWarned()
-    })
-  })
-
-  it('should consider order when casting [Boolean, String] multi-type props', () => {
-    const vm = new Vue({
-      template: '<test ref="test" booleanOrString stringOrBoolean />',
-      components: {
-        test: {
-          template: '<div></div>',
-          props: {
-            booleanOrString: [Boolean, String],
-            stringOrBoolean: [String, Boolean]
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$refs.test.$props.booleanOrString).toBe(true)
-    expect(vm.$refs.test.$props.stringOrBoolean).toBe('')
-  })
-
-  it('should warn when a prop type is not a constructor', () => {
-    new Vue({
-      template: '<div>{{a}}</div>',
-      props: {
-        a: {
-          type: 'String',
-          default: 'test'
-        }
-      }
-    }).$mount()
-    expect(
-      'Invalid prop type: "String" is not a constructor'
-    ).toHaveBeenWarned()
-  })
-
-  // #12930
-  it('should not unwrap prop values that are raw refs', () => {
-    let val
-    const Comp = {
-      props: ['msg'],
-      created() {
-        val = this.msg
-      },
-      render() {}
-    }
-    const r = ref()
-    new Vue({
-      render: h => h(Comp, { props: { msg: r }})
-    }).$mount()
-    expect(val).toBe(r)
-  })
-})
diff --git a/test/unit/features/options/propsData.spec.ts b/test/unit/features/options/propsData.spec.ts
deleted file mode 100644
index bc364581a28..00000000000
--- a/test/unit/features/options/propsData.spec.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import Vue from 'vue'
-
-describe('Options propsData', () => {
-  it('should work', done => {
-    const A = Vue.extend({
-      props: ['a'],
-      template: '<div>{{ a }}</div>'
-    })
-    const vm = new A({
-      propsData: {
-        a: 123
-      }
-    }).$mount()
-    expect(vm.a).toBe(123)
-    expect(vm.$el.textContent).toBe('123')
-    vm.a = 234
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('234')
-    }).then(done)
-  })
-
-  it('warn non instantiation usage', () => {
-    Vue.extend({
-      propsData: {
-        a: 123
-      }
-    })
-    expect(
-      'option "propsData" can only be used during instance creation'
-    ).toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/options/render.spec.ts b/test/unit/features/options/render.spec.ts
deleted file mode 100644
index 7bccefdc1f1..00000000000
--- a/test/unit/features/options/render.spec.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import Vue from 'vue'
-
-describe('Options render', () => {
-  it('basic usage', () => {
-    const vm = new Vue({
-      render(h) {
-        const children: any[] = []
-        for (let i = 0; i < this.items.length; i++) {
-          children.push(h('li', { staticClass: 'task' }, [this.items[i].name]))
-        }
-        return h('ul', { staticClass: 'tasks' }, children)
-      },
-      data: {
-        items: [
-          { id: 1, name: 'task1' },
-          { id: 2, name: 'task2' }
-        ]
-      }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('UL')
-    for (let i = 0; i < vm.$el.children.length; i++) {
-      const li = vm.$el.children[i]
-      expect(li.tagName).toBe('LI')
-      expect(li.textContent).toBe(vm.items[i].name)
-    }
-  })
-
-  it('allow null data', () => {
-    const vm = new Vue({
-      render(h) {
-        return h('div', null, 'hello' /* string as children*/)
-      }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('DIV')
-    expect(vm.$el.textContent).toBe('hello')
-  })
-
-  it('should warn non `render` option and non `template` option', () => {
-    new Vue().$mount()
-    expect(
-      'Failed to mount component: template or render function not defined.'
-    ).toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/options/renderError.spec.ts b/test/unit/features/options/renderError.spec.ts
deleted file mode 100644
index 6ca0a590f58..00000000000
--- a/test/unit/features/options/renderError.spec.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import Vue from 'vue'
-
-describe('Options renderError', () => {
-  it('should be used on render errors', done => {
-    Vue.config.errorHandler = () => {}
-    const vm = new Vue({
-      data: {
-        ok: true
-      },
-      render(h) {
-        if (this.ok) {
-          return h('div', 'ok')
-        } else {
-          throw new Error('no')
-        }
-      },
-      renderError(h, err) {
-        return h('div', err.toString())
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('ok')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('Error: no')
-      Vue.config.errorHandler = undefined
-    }).then(done)
-  })
-
-  it('should pass on errors in renderError to global handler', () => {
-    const spy = (Vue.config.errorHandler = vi.fn())
-    const err = new Error('renderError')
-    const vm = new Vue({
-      render() {
-        throw new Error('render')
-      },
-      renderError() {
-        throw err
-      }
-    }).$mount()
-    expect(spy).toHaveBeenCalledWith(err, vm, 'renderError')
-  })
-})
diff --git a/test/unit/features/options/template.spec.ts b/test/unit/features/options/template.spec.ts
deleted file mode 100644
index e4ed15a38c1..00000000000
--- a/test/unit/features/options/template.spec.ts
+++ /dev/null
@@ -1,96 +0,0 @@
-import Vue from 'vue'
-
-describe('Options template', () => {
-  let el
-  beforeEach(() => {
-    el = document.createElement('script')
-    el.type = 'x-template'
-    el.id = 'app'
-    el.innerHTML = '<p>{{message}}</p>'
-    document.body.appendChild(el)
-  })
-
-  afterEach(() => {
-    document.body.removeChild(el)
-  })
-
-  it('basic usage', () => {
-    const vm = new Vue({
-      template: '<div>{{message}}</div>',
-      data: { message: 'hello world' }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('DIV')
-    expect(vm.$el.textContent).toBe(vm.message)
-  })
-
-  it('id reference', () => {
-    const vm = new Vue({
-      template: '#app',
-      data: { message: 'hello world' }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('P')
-    expect(vm.$el.textContent).toBe(vm.message)
-  })
-
-  it('DOM element', () => {
-    const elm = document.createElement('p')
-    elm.innerHTML = '<p>{{message}}</p>'
-    const vm = new Vue({
-      template: elm,
-      data: { message: 'hello world' }
-    }).$mount()
-    expect(vm.$el.tagName).toBe('P')
-    expect(vm.$el.textContent).toBe(vm.message)
-  })
-
-  it('invalid template', () => {
-    new Vue({
-      template: Vue,
-      data: { message: 'hello world' }
-    }).$mount()
-    expect('invalid template option').toHaveBeenWarned()
-  })
-
-  it('warn error in generated function', () => {
-    new Vue({
-      template:
-        '<div v-if="!@"><span>{{ a"" }}</span><span>{{ do + 1 }}</span></div>'
-    }).$mount()
-    expect('Error compiling template').toHaveBeenWarned()
-    expect('Raw expression: v-if="!@"').toHaveBeenWarned()
-    expect('Raw expression: {{ a"" }}').toHaveBeenWarned()
-    expect(
-      'avoid using JavaScript keyword as property name: "do"'
-    ).toHaveBeenWarned()
-  })
-
-  it('should not warn $ prefixed keywords', () => {
-    new Vue({
-      template: `<div @click="$delete(foo, 'bar')"></div>`
-    }).$mount()
-    expect(
-      'avoid using JavaScript keyword as property name'
-    ).not.toHaveBeenWarned()
-  })
-
-  it('warn error in generated function (v-for)', () => {
-    new Vue({
-      template: '<div><div v-for="(1, 2) in a----"></div></div>'
-    }).$mount()
-    expect('Error compiling template').toHaveBeenWarned()
-    expect('invalid v-for alias "1"').toHaveBeenWarned()
-    expect('invalid v-for iterator "2"').toHaveBeenWarned()
-    expect('Raw expression: v-for="(1, 2) in a----"').toHaveBeenWarned()
-  })
-
-  it('warn error in generated function (v-on)', () => {
-    new Vue({
-      template: `<div @click="delete('Delete')"></div>`,
-      methods: { delete: function () {} }
-    }).$mount()
-    expect('Error compiling template').toHaveBeenWarned()
-    expect(
-      `avoid using JavaScript unary operator as property name: "delete()" in expression @click="delete('Delete')"`
-    ).toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/options/watch.spec.ts b/test/unit/features/options/watch.spec.ts
deleted file mode 100644
index d092e21224b..00000000000
--- a/test/unit/features/options/watch.spec.ts
+++ /dev/null
@@ -1,179 +0,0 @@
-import Vue from 'vue'
-import testObjectOption from '../../../helpers/test-object-option'
-
-describe('Options watch', () => {
-  let spy
-  beforeEach(() => {
-    spy = vi.fn()
-  })
-
-  testObjectOption('watch')
-
-  it('basic usage', done => {
-    const vm = new Vue({
-      data: {
-        a: 1
-      },
-      watch: {
-        a: spy
-      }
-    })
-    expect(spy).not.toHaveBeenCalled()
-    vm.a = 2
-    expect(spy).not.toHaveBeenCalled()
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(2, 1)
-    }).then(done)
-  })
-
-  it('string method name', done => {
-    const vm = new Vue({
-      data: {
-        a: 1
-      },
-      watch: {
-        a: 'onChange'
-      },
-      methods: {
-        onChange: spy
-      }
-    })
-    expect(spy).not.toHaveBeenCalled()
-    vm.a = 2
-    expect(spy).not.toHaveBeenCalled()
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(2, 1)
-    }).then(done)
-  })
-
-  it('multiple cbs (after option merge)', done => {
-    const spy1 = vi.fn()
-    const Test = Vue.extend({
-      watch: {
-        a: spy1
-      }
-    })
-    const vm = new Test({
-      data: { a: 1 },
-      watch: {
-        a: spy
-      }
-    })
-    vm.a = 2
-    waitForUpdate(() => {
-      expect(spy1).toHaveBeenCalledWith(2, 1)
-      expect(spy).toHaveBeenCalledWith(2, 1)
-    }).then(done)
-  })
-
-  it('with option: immediate', done => {
-    const vm = new Vue({
-      data: { a: 1 },
-      watch: {
-        a: {
-          handler: spy,
-          immediate: true
-        }
-      }
-    })
-    expect(spy).toHaveBeenCalledWith(1)
-    vm.a = 2
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(2, 1)
-    }).then(done)
-  })
-
-  it('with option: deep', done => {
-    const vm = new Vue({
-      data: { a: { b: 1 } },
-      watch: {
-        a: {
-          handler: spy,
-          deep: true
-        }
-      }
-    })
-    const oldA = vm.a
-    expect(spy).not.toHaveBeenCalled()
-    vm.a.b = 2
-    expect(spy).not.toHaveBeenCalled()
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(vm.a, vm.a)
-      vm.a = { b: 3 }
-    })
-      .then(() => {
-        expect(spy).toHaveBeenCalledWith(vm.a, oldA)
-      })
-      .then(done)
-  })
-
-  it('correctly merges multiple extends', done => {
-    const spy2 = vi.fn()
-    const spy3 = vi.fn()
-    const A = Vue.extend({
-      data: function () {
-        return {
-          a: 0,
-          b: 0
-        }
-      },
-      watch: {
-        b: spy
-      }
-    })
-
-    const B = Vue.extend({
-      extends: A,
-      watch: {
-        a: spy2
-      }
-    })
-
-    const C = Vue.extend({
-      extends: B,
-      watch: {
-        a: spy3
-      }
-    })
-
-    const vm = new C()
-    vm.a = 1
-
-    waitForUpdate(() => {
-      expect(spy).not.toHaveBeenCalled()
-      expect(spy2).toHaveBeenCalledWith(1, 0)
-      expect(spy3).toHaveBeenCalledWith(1, 0)
-    }).then(done)
-  })
-
-  it('should support watching unicode paths', done => {
-    const vm = new Vue({
-      data: {
-        数据: 1
-      },
-      watch: {
-        数据: spy
-      }
-    })
-    expect(spy).not.toHaveBeenCalled()
-    vm['数据'] = 2
-    expect(spy).not.toHaveBeenCalled()
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(2, 1)
-    }).then(done)
-  })
-
-  it('should not warn proper usage', () => {
-    new Vue({
-      data: {
-        foo: { _bar: 1 }, // element has such watchers...
-        prop1: 123
-      },
-      watch: {
-        'foo._bar': () => {},
-        prop1() {}
-      }
-    })
-    expect(`Failed watching path`).not.toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/template-ref.spec.ts b/test/unit/features/template-ref.spec.ts
deleted file mode 100644
index ce3ee2b2034..00000000000
--- a/test/unit/features/template-ref.spec.ts
+++ /dev/null
@@ -1,252 +0,0 @@
-import Vue from 'vue'
-
-describe('ref', () => {
-  type TestShape = {
-    id: string
-    template: string
-    data?: any
-    components?: any
-  }
-  type ComponentShape = {
-    test: TestShape
-    test2: TestShape
-    test3: TestShape
-  }
-  const components: ComponentShape = {
-    test: {
-      id: 'test',
-      template: '<div>test</div>'
-    },
-    test2: {
-      id: 'test2',
-      template: '<div>test2</div>'
-    },
-    test3: {
-      id: 'test3',
-      template: '<div>test3</div>'
-    }
-  }
-
-  it('should work', () => {
-    const vm = new Vue({
-      data: {
-        value: 'bar'
-      },
-      template: `<div>
-        <test ref="foo"></test>
-        <test2 :ref="value"></test2>
-        <test3 :ref="0"></test3>
-      </div>`,
-      components
-    })
-    vm.$mount()
-    expect(vm.$refs.foo).toBeTruthy()
-    expect(vm.$refs.foo.$options.id).toBe('test')
-    expect(vm.$refs.bar).toBeTruthy()
-    expect(vm.$refs.bar.$options.id).toBe('test2')
-    expect(vm.$refs['0']).toBeTruthy()
-    expect(vm.$refs['0'].$options.id).toBe('test3')
-  })
-
-  it('should dynamically update refs', done => {
-    const vm = new Vue({
-      data: {
-        value: 'foo'
-      },
-      template: '<div :ref="value"></div>'
-    }).$mount()
-    expect(vm.$refs.foo).toBe(vm.$el)
-    vm.value = 'bar'
-    waitForUpdate(() => {
-      expect(vm.$refs.foo).toBe(undefined)
-      expect(vm.$refs.bar).toBe(vm.$el)
-    }).then(done)
-  })
-
-  it('should work as a hyperscript prop', () => {
-    const vm = new Vue({
-      components,
-      render(h) {
-        return h('div', null, [h('test', { ref: 'test' })])
-      }
-    })
-    vm.$mount()
-    expect(vm.$refs.test).toBeTruthy()
-    expect(vm.$refs.test.$options.id).toBe('test')
-  })
-
-  it('should accept HOC component', () => {
-    const vm = new Vue({
-      template: '<test ref="test"></test>',
-      components
-    })
-    vm.$mount()
-    expect(vm.$refs.test).toBeTruthy()
-    expect(vm.$refs.test.$options.id).toBe('test')
-  })
-
-  it('should accept dynamic component', done => {
-    const vm = new Vue({
-      template: `<div>
-        <component :is="test" ref="test"></component>
-      </div>`,
-      components,
-      data: { test: 'test' }
-    })
-    vm.$mount()
-    expect(vm.$refs.test.$options.id).toBe('test')
-    vm.test = 'test2'
-    waitForUpdate(() => {
-      expect(vm.$refs.test.$options.id).toBe('test2')
-      vm.test = ''
-    })
-      .then(() => {
-        expect(vm.$refs.test).toBe(undefined)
-      })
-      .then(done)
-  })
-
-  it('should register as Array when used with v-for', done => {
-    const vm = new Vue({
-      data: {
-        items: [1, 2, 3]
-      },
-      template: `
-        <div>
-          <div v-for="n in items" ref="list">{{n}}</div>
-        </div>
-      `
-    }).$mount()
-    assertRefs()
-    // updating
-    vm.items.push(4)
-    waitForUpdate(assertRefs)
-      .then(() => {
-        vm.items = []
-      })
-      .then(assertRefs)
-      .then(done)
-
-    function assertRefs() {
-      expect(Array.isArray(vm.$refs.list)).toBe(true)
-      expect(vm.$refs.list.length).toBe(vm.items.length)
-      expect(
-        vm.$refs.list.every((item, i) => item.textContent === String(i + 1))
-      ).toBe(true)
-    }
-  })
-
-  it('should register as Array when used with v-for (components)', done => {
-    const vm = new Vue({
-      data: {
-        items: [1, 2, 3]
-      },
-      template: `
-        <div>
-          <test v-for="n in items" ref="list" :key="n" :n="n"></test>
-        </div>
-      `,
-      components: {
-        test: {
-          props: ['n'],
-          template: '<div>{{ n }}</div>'
-        }
-      }
-    }).$mount()
-    assertRefs()
-    // updating
-    vm.items.push(4)
-    waitForUpdate(assertRefs)
-      .then(() => {
-        vm.items = []
-      })
-      .then(assertRefs)
-      .then(done)
-
-    function assertRefs() {
-      expect(Array.isArray(vm.$refs.list)).toBe(true)
-      expect(vm.$refs.list.length).toBe(vm.items.length)
-      expect(
-        vm.$refs.list.every((comp, i) => comp.$el.textContent === String(i + 1))
-      ).toBe(true)
-    }
-  })
-
-  it('should work with v-for on dynamic component', done => {
-    components.test3 = {
-      id: 'test3',
-      template: `<test1 v-if="!normal"></test1><div v-else>test3</div>`,
-      data() {
-        return { normal: false }
-      },
-      components: { test1: components.test }
-    }
-    // a flag that representing whether to test component content or not
-    let testContent = false
-
-    const vm = new Vue({
-      template: `
-        <div>
-          <component
-            v-for="(item, index) in items"
-            :key="index"
-            :is="item"
-            ref="children">
-          </component>
-        </div>
-      `,
-      data: {
-        items: ['test2', 'test3']
-      },
-      components
-    }).$mount()
-    assertRefs()
-    expect(vm.$refs.children[0].$el.textContent).toBe('test2')
-    expect(vm.$refs.children[1].$el.textContent).toBe('test')
-    // updating
-    vm.$refs.children[1].normal = true
-    testContent = true
-    waitForUpdate(assertRefs)
-      .then(() => {
-        vm.items.push('test')
-      })
-      .then(assertRefs)
-      .then(done)
-
-    function assertRefs() {
-      expect(Array.isArray(vm.$refs.children)).toBe(true)
-      expect(vm.$refs.children.length).toBe(vm.items.length)
-      if (testContent) {
-        expect(
-          vm.$refs.children.every(
-            (comp, i) => comp.$el.textContent === vm.items[i]
-          )
-        ).toBe(true)
-      }
-    }
-  })
-
-  it('should register on component with empty roots', done => {
-    const vm = new Vue({
-      template: '<child ref="test"></child>',
-      components: {
-        child: {
-          template: '<div v-if="show"></div>',
-          data() {
-            return { show: false }
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$refs.test).toBe(vm.$children[0])
-    vm.$refs.test.show = true
-    waitForUpdate(() => {
-      expect(vm.$refs.test).toBe(vm.$children[0])
-      vm.$refs.test.show = false
-    })
-      .then(() => {
-        expect(vm.$refs.test).toBe(vm.$children[0])
-      })
-      .then(done)
-  })
-})
diff --git a/test/unit/features/v3/apiAsyncComponent.spec.ts b/test/unit/features/v3/apiAsyncComponent.spec.ts
deleted file mode 100644
index 2970738f9d5..00000000000
--- a/test/unit/features/v3/apiAsyncComponent.spec.ts
+++ /dev/null
@@ -1,241 +0,0 @@
-import Vue from 'vue'
-import { defineAsyncComponent, h, ref, nextTick, defineComponent } from 'v3'
-import { Component } from 'types/component'
-
-const timeout = (n: number = 0) => new Promise(r => setTimeout(r, n))
-
-const loadingComponent = defineComponent({
-  template: `<div>loading</div>`
-})
-
-const resolvedComponent = defineComponent({
-  template: `<div>resolved</div>`
-})
-
-describe('api: defineAsyncComponent', () => {
-  afterEach(() => {
-    Vue.config.errorHandler = undefined
-  })
-
-  test('simple usage', async () => {
-    let resolve: (comp: Component) => void
-    const Foo = defineAsyncComponent(
-      () =>
-        new Promise(r => {
-          resolve = r as any
-        })
-    )
-
-    const toggle = ref(true)
-
-    const vm = new Vue({
-      render: () => (toggle.value ? h(Foo) : null)
-    }).$mount()
-
-    expect(vm.$el.nodeType).toBe(8)
-
-    resolve!(resolvedComponent)
-    // first time resolve, wait for macro task since there are multiple
-    // microtasks / .then() calls
-    await timeout()
-    expect(vm.$el.innerHTML).toBe('resolved')
-
-    toggle.value = false
-    await nextTick()
-    expect(vm.$el.nodeType).toBe(8)
-
-    // already resolved component should update on nextTick
-    toggle.value = true
-    await nextTick()
-    expect(vm.$el.innerHTML).toBe('resolved')
-  })
-
-  test('with loading component', async () => {
-    let resolve: (comp: Component) => void
-    const Foo = defineAsyncComponent({
-      loader: () =>
-        new Promise(r => {
-          resolve = r as any
-        }),
-      loadingComponent,
-      delay: 1 // defaults to 200
-    })
-
-    const toggle = ref(true)
-
-    const vm = new Vue({
-      render: () => (toggle.value ? h(Foo) : null)
-    }).$mount()
-
-    // due to the delay, initial mount should be empty
-    expect(vm.$el.nodeType).toBe(8)
-
-    // loading show up after delay
-    await timeout(1)
-    expect(vm.$el.innerHTML).toBe('loading')
-
-    resolve!(resolvedComponent)
-    await timeout()
-    expect(vm.$el.innerHTML).toBe('resolved')
-
-    toggle.value = false
-    await nextTick()
-    expect(vm.$el.nodeType).toBe(8)
-
-    // already resolved component should update on nextTick without loading
-    // state
-    toggle.value = true
-    await nextTick()
-    expect(vm.$el.innerHTML).toBe('resolved')
-  })
-
-  test('error with error component', async () => {
-    let reject: (e: Error) => void
-    const Foo = defineAsyncComponent({
-      loader: () =>
-        new Promise((_resolve, _reject) => {
-          reject = _reject
-        }),
-      errorComponent: {
-        template: `<div>errored</div>`
-      }
-    })
-
-    const toggle = ref(true)
-
-    const vm = new Vue({
-      render: () => (toggle.value ? h(Foo) : null)
-    }).$mount()
-
-    expect(vm.$el.nodeType).toBe(8)
-
-    const err = new Error('errored')
-    reject!(err)
-    await timeout()
-    expect('Failed to resolve async').toHaveBeenWarned()
-    expect(vm.$el.innerHTML).toBe('errored')
-
-    toggle.value = false
-    await nextTick()
-    expect(vm.$el.nodeType).toBe(8)
-  })
-
-  test('retry (success)', async () => {
-    let loaderCallCount = 0
-    let resolve: (comp: Component) => void
-    let reject: (e: Error) => void
-
-    const Foo = defineAsyncComponent({
-      loader: () => {
-        loaderCallCount++
-        return new Promise((_resolve, _reject) => {
-          resolve = _resolve as any
-          reject = _reject
-        })
-      },
-      onError(error, retry, fail) {
-        if (error.message.match(/foo/)) {
-          retry()
-        } else {
-          fail()
-        }
-      }
-    })
-
-    const vm = new Vue({
-      render: () => h(Foo)
-    }).$mount()
-
-    expect(vm.$el.nodeType).toBe(8)
-    expect(loaderCallCount).toBe(1)
-
-    const err = new Error('foo')
-    reject!(err)
-    await timeout()
-    expect(loaderCallCount).toBe(2)
-    expect(vm.$el.nodeType).toBe(8)
-
-    // should render this time
-    resolve!(resolvedComponent)
-    await timeout()
-    expect(vm.$el.innerHTML).toBe('resolved')
-  })
-
-  test('retry (skipped)', async () => {
-    let loaderCallCount = 0
-    let reject: (e: Error) => void
-
-    const Foo = defineAsyncComponent({
-      loader: () => {
-        loaderCallCount++
-        return new Promise((_resolve, _reject) => {
-          reject = _reject
-        })
-      },
-      onError(error, retry, fail) {
-        if (error.message.match(/bar/)) {
-          retry()
-        } else {
-          fail()
-        }
-      }
-    })
-
-    const vm = new Vue({
-      render: () => h(Foo)
-    }).$mount()
-
-    expect(vm.$el.nodeType).toBe(8)
-    expect(loaderCallCount).toBe(1)
-
-    const err = new Error('foo')
-    reject!(err)
-    await timeout()
-    // should fail because retryWhen returns false
-    expect(loaderCallCount).toBe(1)
-    expect(vm.$el.nodeType).toBe(8)
-    expect('Failed to resolve async').toHaveBeenWarned()
-  })
-
-  test('retry (fail w/ max retry attempts)', async () => {
-    let loaderCallCount = 0
-    let reject: (e: Error) => void
-
-    const Foo = defineAsyncComponent({
-      loader: () => {
-        loaderCallCount++
-        return new Promise((_resolve, _reject) => {
-          reject = _reject
-        })
-      },
-      onError(error, retry, fail, attempts) {
-        if (error.message.match(/foo/) && attempts <= 1) {
-          retry()
-        } else {
-          fail()
-        }
-      }
-    })
-
-    const vm = new Vue({
-      render: () => h(Foo)
-    }).$mount()
-
-    expect(vm.$el.nodeType).toBe(8)
-    expect(loaderCallCount).toBe(1)
-
-    // first retry
-    const err = new Error('foo')
-    reject!(err)
-    await timeout()
-    expect(loaderCallCount).toBe(2)
-    expect(vm.$el.nodeType).toBe(8)
-
-    // 2nd retry, should fail due to reaching maxRetries
-    reject!(err)
-    await timeout()
-    expect(loaderCallCount).toBe(2)
-    expect(vm.$el.nodeType).toBe(8)
-    expect('Failed to resolve async').toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/v3/apiInject.spec.ts b/test/unit/features/v3/apiInject.spec.ts
deleted file mode 100644
index c467d085a55..00000000000
--- a/test/unit/features/v3/apiInject.spec.ts
+++ /dev/null
@@ -1,336 +0,0 @@
-import Vue from 'vue'
-import {
-  h,
-  provide,
-  inject,
-  InjectionKey,
-  ref,
-  nextTick,
-  Ref,
-  readonly,
-  reactive
-} from 'v3/index'
-
-// reference: https://vue-composition-api-rfc.netlify.com/api.html#provide-inject
-describe('api: provide/inject', () => {
-  it('string keys', () => {
-    const Provider = {
-      setup() {
-        provide('foo', 1)
-        return () => h(Middle)
-      }
-    }
-
-    const Middle = {
-      render: () => h(Consumer)
-    }
-
-    const Consumer = {
-      setup() {
-        const foo = inject('foo')
-        return () => h('div', foo)
-      }
-    }
-
-    const vm = new Vue(Provider).$mount()
-    expect(vm.$el.outerHTML).toBe(`<div>1</div>`)
-  })
-
-  it('symbol keys', () => {
-    // also verifies InjectionKey type sync
-    const key: InjectionKey<number> = Symbol()
-
-    const Provider = {
-      setup() {
-        provide(key, 1)
-        return () => h(Middle)
-      }
-    }
-
-    const Middle = {
-      render: () => h(Consumer)
-    }
-
-    const Consumer = {
-      setup() {
-        const foo = inject(key) || 1
-        return () => h('div', foo + 1)
-      }
-    }
-
-    const vm = new Vue(Provider).$mount()
-    expect(vm.$el.outerHTML).toBe(`<div>2</div>`)
-  })
-
-  it('default values', () => {
-    const Provider = {
-      setup() {
-        provide('foo', 'foo')
-        return () => h(Middle)
-      }
-    }
-
-    const Middle = {
-      render: () => h(Consumer)
-    }
-
-    const Consumer = {
-      setup() {
-        // default value should be ignored if value is provided
-        const foo = inject('foo', 'fooDefault')
-        // default value should be used if value is not provided
-        const bar = inject('bar', 'bar')
-        return () => h('div', foo + bar)
-      }
-    }
-
-    const vm = new Vue(Provider).$mount()
-    expect(vm.$el.outerHTML).toBe(`<div>foobar</div>`)
-  })
-
-  it('bound to instance', () => {
-    const Provider = {
-      setup() {
-        return () => h(Consumer)
-      }
-    }
-
-    const Consumer = {
-      name: 'Consumer',
-      inject: {
-        foo: {
-          from: 'foo',
-          default() {
-            return this!.$options.name
-          }
-        }
-      },
-      render() {
-        // @ts-ignore
-        return h('div', this.foo)
-      }
-    }
-
-    const vm = new Vue(Provider).$mount()
-    expect(vm.$el.outerHTML).toBe(`<div>Consumer</div>`)
-  })
-
-  it('nested providers', () => {
-    const ProviderOne = {
-      setup() {
-        provide('foo', 'foo')
-        provide('bar', 'bar')
-        return () => h(ProviderTwo)
-      }
-    }
-
-    const ProviderTwo = {
-      setup() {
-        // override parent value
-        provide('foo', 'fooOverride')
-        provide('baz', 'baz')
-        return () => h(Consumer)
-      }
-    }
-
-    const Consumer = {
-      setup() {
-        const foo = inject('foo')
-        const bar = inject('bar')
-        const baz = inject('baz')
-        return () => h('div', [foo, bar, baz].join(','))
-      }
-    }
-
-    const vm = new Vue(ProviderOne).$mount()
-    expect(vm.$el.outerHTML).toBe(`<div>fooOverride,bar,baz</div>`)
-  })
-
-  it('reactivity with refs', async () => {
-    const count = ref(1)
-
-    const Provider = {
-      setup() {
-        provide('count', count)
-        return () => h(Middle)
-      }
-    }
-
-    const Middle = {
-      render: () => h(Consumer)
-    }
-
-    const Consumer = {
-      setup() {
-        const count = inject<Ref<number>>('count')!
-        return () => h('div', count.value)
-      }
-    }
-
-    const vm = new Vue(Provider).$mount()
-    expect(vm.$el.outerHTML).toBe(`<div>1</div>`)
-
-    count.value++
-    await nextTick()
-    expect(vm.$el.outerHTML).toBe(`<div>2</div>`)
-  })
-
-  it('reactivity with readonly refs', async () => {
-    const count = ref(1)
-
-    const Provider = {
-      setup() {
-        provide('count', readonly(count))
-        return () => h(Middle)
-      }
-    }
-
-    const Middle = {
-      render: () => h(Consumer)
-    }
-
-    const Consumer = {
-      setup() {
-        const count = inject<Ref<number>>('count')!
-        // should not work
-        count.value++
-        return () => h('div', count.value)
-      }
-    }
-
-    const vm = new Vue(Provider).$mount()
-    expect(vm.$el.outerHTML).toBe(`<div>1</div>`)
-
-    expect(
-      `Set operation on key "value" failed: target is readonly`
-    ).toHaveBeenWarned()
-
-    // source mutation should still work
-    count.value++
-    await nextTick()
-    expect(vm.$el.outerHTML).toBe(`<div>2</div>`)
-  })
-
-  it('reactivity with objects', async () => {
-    const rootState = reactive({ count: 1 })
-
-    const Provider = {
-      setup() {
-        provide('state', rootState)
-        return () => h(Middle)
-      }
-    }
-
-    const Middle = {
-      render: () => h(Consumer)
-    }
-
-    const Consumer = {
-      setup() {
-        const state = inject<typeof rootState>('state')!
-        return () => h('div', state.count)
-      }
-    }
-
-    const vm = new Vue(Provider).$mount()
-    expect(vm.$el.outerHTML).toBe(`<div>1</div>`)
-
-    rootState.count++
-    await nextTick()
-    expect(vm.$el.outerHTML).toBe(`<div>2</div>`)
-  })
-
-  it('reactivity with readonly objects', async () => {
-    const rootState = reactive({ count: 1 })
-
-    const Provider = {
-      setup() {
-        provide('state', readonly(rootState))
-        return () => h(Middle)
-      }
-    }
-
-    const Middle = {
-      render: () => h(Consumer)
-    }
-
-    const Consumer = {
-      setup() {
-        const state = inject<typeof rootState>('state')!
-        // should not work
-        state.count++
-        return () => h('div', state.count)
-      }
-    }
-
-    const vm = new Vue(Provider).$mount()
-    expect(vm.$el.outerHTML).toBe(`<div>1</div>`)
-
-    expect(
-      `Set operation on key "count" failed: target is readonly`
-    ).toHaveBeenWarned()
-
-    rootState.count++
-    await nextTick()
-    expect(vm.$el.outerHTML).toBe(`<div>2</div>`)
-  })
-
-  it('should warn unfound', () => {
-    const Provider = {
-      setup() {
-        return () => h(Middle)
-      }
-    }
-
-    const Middle = {
-      render: () => h(Consumer)
-    }
-
-    const Consumer = {
-      setup() {
-        const foo = inject('foo')
-        expect(foo).toBeUndefined()
-        return () => h('div', foo)
-      }
-    }
-
-    const vm = new Vue(Provider).$mount()
-    expect(vm.$el.outerHTML).toBe(`<div></div>`)
-    expect(`injection "foo" not found.`).toHaveBeenWarned()
-  })
-
-  it('should not warn when default value is undefined', () => {
-    const Provider = {
-      setup() {
-        return () => h(Middle)
-      }
-    }
-
-    const Middle = {
-      render: () => h(Consumer)
-    }
-
-    const Consumer = {
-      setup() {
-        const foo = inject('foo', undefined)
-        return () => h('div', foo)
-      }
-    }
-
-    new Vue(Provider).$mount()
-    expect(`injection "foo" not found.`).not.toHaveBeenWarned()
-  })
-
-  // #2400
-  it('should not self-inject', () => {
-    const Comp = {
-      setup() {
-        provide('foo', 'foo')
-        const injection = inject('foo', null)
-        return () => h('div', injection)
-      }
-    }
-
-    expect(new Vue(Comp).$mount().$el.outerHTML).toBe(`<div></div>`)
-  })
-})
diff --git a/test/unit/features/v3/apiLifecycle.spec.ts b/test/unit/features/v3/apiLifecycle.spec.ts
deleted file mode 100644
index 21eb33272e3..00000000000
--- a/test/unit/features/v3/apiLifecycle.spec.ts
+++ /dev/null
@@ -1,360 +0,0 @@
-import Vue from 'vue'
-import {
-  h,
-  onBeforeMount,
-  onMounted,
-  ref,
-  reactive,
-  onBeforeUpdate,
-  onUpdated,
-  onBeforeUnmount,
-  onUnmounted,
-  onRenderTracked,
-  onRenderTriggered,
-  DebuggerEvent,
-  TrackOpTypes,
-  TriggerOpTypes
-} from 'v3'
-import { nextTick } from 'core/util'
-
-describe('api: lifecycle hooks', () => {
-  it('onBeforeMount', () => {
-    const fn = vi.fn(() => {
-      // should be called before root is replaced
-      expect(vm.$el).toBeUndefined()
-    })
-
-    const Comp = {
-      setup() {
-        onBeforeMount(fn)
-        return () => h('div', 'hello')
-      }
-    }
-    const vm = new Vue(Comp)
-    vm.$mount()
-    expect(fn).toHaveBeenCalledTimes(1)
-    expect(vm.$el.innerHTML).toBe(`hello`)
-  })
-
-  it('onMounted', () => {
-    const fn = vi.fn(() => {
-      // should be called after inner div is rendered
-      expect(vm.$el.outerHTML).toBe(`<div></div>`)
-    })
-
-    const Comp = {
-      setup() {
-        onMounted(fn)
-        return () => h('div')
-      }
-    }
-    const vm = new Vue(Comp)
-    vm.$mount()
-    expect(fn).toHaveBeenCalledTimes(1)
-  })
-
-  it('onBeforeUpdate', async () => {
-    const count = ref(0)
-    const fn = vi.fn(() => {
-      // should be called before inner div is updated
-      expect(vm.$el.outerHTML).toBe(`<div>0</div>`)
-    })
-
-    const Comp = {
-      setup() {
-        onBeforeUpdate(fn)
-        return () => h('div', count.value)
-      }
-    }
-    const vm = new Vue(Comp).$mount()
-
-    count.value++
-    await nextTick()
-    expect(fn).toHaveBeenCalledTimes(1)
-    expect(vm.$el.outerHTML).toBe(`<div>1</div>`)
-  })
-
-  it('state mutation in onBeforeUpdate', async () => {
-    const count = ref(0)
-    const fn = vi.fn(() => {
-      // should be called before inner div is updated
-      expect(vm.$el.outerHTML).toBe(`<div>0</div>`)
-      count.value++
-    })
-    const renderSpy = vi.fn()
-
-    const Comp = {
-      setup() {
-        onBeforeUpdate(fn)
-        return () => {
-          renderSpy()
-          return h('div', count.value)
-        }
-      }
-    }
-    const vm = new Vue(Comp).$mount()
-    expect(renderSpy).toHaveBeenCalledTimes(1)
-
-    count.value++
-    await nextTick()
-    expect(fn).toHaveBeenCalledTimes(1)
-    expect(renderSpy).toHaveBeenCalledTimes(2)
-    expect(vm.$el.outerHTML).toBe(`<div>2</div>`)
-  })
-
-  it('onUpdated', async () => {
-    const count = ref(0)
-    const fn = vi.fn(() => {
-      // should be called after inner div is updated
-      expect(vm.$el.outerHTML).toBe(`<div>1</div>`)
-    })
-
-    const Comp = {
-      setup() {
-        onUpdated(fn)
-        return () => h('div', count.value)
-      }
-    }
-    const vm = new Vue(Comp).$mount()
-
-    count.value++
-    await nextTick()
-    expect(fn).toHaveBeenCalledTimes(1)
-  })
-
-  it('onBeforeUnmount', async () => {
-    const toggle = ref(true)
-    const root = document.createElement('div')
-    const fn = vi.fn(() => {
-      // should be called before inner div is removed
-      expect(root.outerHTML).toBe(`<div></div>`)
-    })
-
-    const Comp = {
-      setup() {
-        return () => (toggle.value ? h(Child) : null)
-      }
-    }
-
-    const Child = {
-      setup() {
-        onBeforeUnmount(fn)
-        return () => h('div')
-      }
-    }
-
-    new Vue(Comp).$mount(root)
-
-    toggle.value = false
-    await nextTick()
-    expect(fn).toHaveBeenCalledTimes(1)
-  })
-
-  it('onUnmounted', async () => {
-    const toggle = ref(true)
-    const fn = vi.fn(() => {
-      // @discrepancy should be called after inner div is removed
-      // expect(vm.$el.outerHTML).toBe(`<span></span>`)
-    })
-
-    const Comp = {
-      setup() {
-        return () => (toggle.value ? h(Child) : h('span'))
-      }
-    }
-
-    const Child = {
-      setup() {
-        onUnmounted(fn)
-        return () => h('div')
-      }
-    }
-
-    new Vue(Comp).$mount()
-
-    toggle.value = false
-    await nextTick()
-    expect(fn).toHaveBeenCalledTimes(1)
-  })
-
-  it('onBeforeUnmount in onMounted', async () => {
-    const toggle = ref(true)
-    const fn = vi.fn(() => {
-      // should be called before inner div is removed
-      expect(vm.$el.outerHTML).toBe(`<div></div>`)
-    })
-
-    const Comp = {
-      setup() {
-        return () => (toggle.value ? h(Child) : null)
-      }
-    }
-
-    const Child = {
-      setup() {
-        onMounted(() => {
-          onBeforeUnmount(fn)
-        })
-        return () => h('div')
-      }
-    }
-
-    const vm = new Vue(Comp).$mount()
-
-    toggle.value = false
-    await nextTick()
-    expect(fn).toHaveBeenCalledTimes(1)
-  })
-
-  it('lifecycle call order', async () => {
-    const count = ref(0)
-    const calls: string[] = []
-
-    const Root = {
-      setup() {
-        onBeforeMount(() => calls.push('root onBeforeMount'))
-        onMounted(() => calls.push('root onMounted'))
-        onBeforeUpdate(() => calls.push('root onBeforeUpdate'))
-        onUpdated(() => calls.push('root onUpdated'))
-        onBeforeUnmount(() => calls.push('root onBeforeUnmount'))
-        onUnmounted(() => calls.push('root onUnmounted'))
-        return () => h(Mid, { props: { count: count.value } })
-      }
-    }
-
-    const Mid = {
-      props: ['count'],
-      setup(props: any) {
-        onBeforeMount(() => calls.push('mid onBeforeMount'))
-        onMounted(() => calls.push('mid onMounted'))
-        onBeforeUpdate(() => calls.push('mid onBeforeUpdate'))
-        onUpdated(() => calls.push('mid onUpdated'))
-        onBeforeUnmount(() => calls.push('mid onBeforeUnmount'))
-        onUnmounted(() => calls.push('mid onUnmounted'))
-        return () => h(Child, { props: { count: props.count } })
-      }
-    }
-
-    const Child = {
-      props: ['count'],
-      setup(props: any) {
-        onBeforeMount(() => calls.push('child onBeforeMount'))
-        onMounted(() => calls.push('child onMounted'))
-        onBeforeUpdate(() => calls.push('child onBeforeUpdate'))
-        onUpdated(() => calls.push('child onUpdated'))
-        onBeforeUnmount(() => calls.push('child onBeforeUnmount'))
-        onUnmounted(() => calls.push('child onUnmounted'))
-        return () => h('div', props.count)
-      }
-    }
-
-    // mount
-    const vm = new Vue(Root)
-    vm.$mount()
-    expect(calls).toEqual([
-      'root onBeforeMount',
-      'mid onBeforeMount',
-      'child onBeforeMount',
-      'child onMounted',
-      'mid onMounted',
-      'root onMounted'
-    ])
-
-    calls.length = 0
-
-    // update
-    count.value++
-    await nextTick()
-    expect(calls).toEqual([
-      'root onBeforeUpdate',
-      'mid onBeforeUpdate',
-      'child onBeforeUpdate',
-      'child onUpdated',
-      'mid onUpdated',
-      'root onUpdated'
-    ])
-
-    calls.length = 0
-
-    // unmount
-    vm.$destroy()
-    expect(calls).toEqual([
-      'root onBeforeUnmount',
-      'mid onBeforeUnmount',
-      'child onBeforeUnmount',
-      'child onUnmounted',
-      'mid onUnmounted',
-      'root onUnmounted'
-    ])
-  })
-
-  it('onRenderTracked', () => {
-    const events: DebuggerEvent[] = []
-    const onTrack = vi.fn((e: DebuggerEvent) => {
-      events.push(e)
-    })
-    const obj = reactive({ foo: 1, bar: 2 })
-
-    const Comp = {
-      setup() {
-        onRenderTracked(onTrack)
-        return () => h('div', [obj.foo + obj.bar])
-      }
-    }
-
-    new Vue(Comp).$mount()
-    expect(onTrack).toHaveBeenCalledTimes(2)
-    expect(events).toMatchObject([
-      {
-        target: obj,
-        type: TrackOpTypes.GET,
-        key: 'foo'
-      },
-      {
-        target: obj,
-        type: TrackOpTypes.GET,
-        key: 'bar'
-      }
-    ])
-  })
-
-  it('onRenderTriggered', async () => {
-    const events: DebuggerEvent[] = []
-    const onTrigger = vi.fn((e: DebuggerEvent) => {
-      events.push(e)
-    })
-    const obj = reactive<{
-      foo: number
-      bar: number
-    }>({ foo: 1, bar: 2 })
-
-    const Comp = {
-      setup() {
-        onRenderTriggered(onTrigger)
-        return () => h('div', [obj.foo + obj.bar])
-      }
-    }
-
-    new Vue(Comp).$mount()
-
-    obj.foo++
-    await nextTick()
-    expect(onTrigger).toHaveBeenCalledTimes(1)
-    expect(events[0]).toMatchObject({
-      type: TriggerOpTypes.SET,
-      key: 'foo',
-      oldValue: 1,
-      newValue: 2
-    })
-
-    obj.bar++
-    await nextTick()
-    expect(onTrigger).toHaveBeenCalledTimes(2)
-    expect(events[1]).toMatchObject({
-      type: TriggerOpTypes.SET,
-      key: 'bar',
-      oldValue: 2,
-      newValue: 3
-    })
-  })
-})
diff --git a/test/unit/features/v3/apiSetup.spec.ts b/test/unit/features/v3/apiSetup.spec.ts
deleted file mode 100644
index 11757878e9c..00000000000
--- a/test/unit/features/v3/apiSetup.spec.ts
+++ /dev/null
@@ -1,336 +0,0 @@
-import { h, ref, reactive, isReactive, toRef, isRef } from 'v3'
-import { nextTick } from 'core/util'
-import { effect } from 'v3/reactivity/effect'
-import Vue from 'vue'
-
-function renderToString(comp: any) {
-  const vm = new Vue(comp).$mount()
-  return vm.$el.outerHTML
-}
-
-describe('api: setup context', () => {
-  it('should expose return values to template render context', () => {
-    const Comp = {
-      setup() {
-        return {
-          // ref should auto-unwrap
-          ref: ref('foo'),
-          // object exposed as-is
-          object: reactive({ msg: 'bar' }),
-          // primitive value exposed as-is
-          value: 'baz'
-        }
-      },
-      render() {
-        return h('div', `${this.ref} ${this.object.msg} ${this.value}`)
-      }
-    }
-    expect(renderToString(Comp)).toMatch(`<div>foo bar baz</div>`)
-  })
-
-  it('should support returning render function', () => {
-    const Comp = {
-      setup() {
-        return () => {
-          return h('div', 'hello')
-        }
-      }
-    }
-    expect(renderToString(Comp)).toMatch(`<div>hello</div>`)
-  })
-
-  it('props', async () => {
-    const count = ref(0)
-    let dummy
-
-    const Parent = {
-      render: () => h(Child, { props: { count: count.value } })
-    }
-
-    const Child = {
-      props: { count: Number },
-      setup(props) {
-        effect(() => {
-          dummy = props.count
-        })
-        return () => h('div', props.count)
-      }
-    }
-
-    const vm = new Vue(Parent).$mount()
-    expect(vm.$el.outerHTML).toMatch(`<div>0</div>`)
-    expect(dummy).toBe(0)
-
-    // props should be reactive
-    count.value++
-    await nextTick()
-    expect(vm.$el.outerHTML).toMatch(`<div>1</div>`)
-    expect(dummy).toBe(1)
-  })
-
-  it('context.attrs', async () => {
-    const toggle = ref(true)
-
-    const Parent = {
-      render: () =>
-        h(Child, { attrs: toggle.value ? { id: 'foo' } : { class: 'baz' } })
-    }
-
-    const Child = {
-      // explicit empty props declaration
-      // puts everything received in attrs
-      // disable implicit fallthrough
-      inheritAttrs: false,
-      setup(_props: any, { attrs }: any) {
-        return () => h('div', { attrs })
-      }
-    }
-
-    const vm = new Vue(Parent).$mount()
-    expect(vm.$el.outerHTML).toMatch(`<div id="foo"></div>`)
-
-    // should update even though it's not reactive
-    toggle.value = false
-    await nextTick()
-    expect(vm.$el.outerHTML).toMatch(`<div class="baz"></div>`)
-  })
-
-  // vuejs/core #4161
-  it('context.attrs in child component slots', async () => {
-    const toggle = ref(true)
-
-    const Wrapper = {
-      template: `<div><slot/></div>`
-    }
-
-    const Child = {
-      inheritAttrs: false,
-      setup(_: any, { attrs }: any) {
-        return () => {
-          return h(Wrapper, [h('div', { attrs })])
-        }
-      }
-    }
-
-    const Parent = {
-      render: () =>
-        h(Child, { attrs: toggle.value ? { id: 'foo' } : { class: 'baz' } })
-    }
-
-    const vm = new Vue(Parent).$mount()
-    expect(vm.$el.outerHTML).toMatch(`<div id="foo"></div>`)
-
-    // should update even though it's not reactive
-    toggle.value = false
-    await nextTick()
-    expect(vm.$el.outerHTML).toMatch(`<div class="baz"></div>`)
-  })
-
-  it('context.attrs in child component scoped slots', async () => {
-    const toggle = ref(true)
-
-    const Wrapper = {
-      template: `<div><slot/></div>`
-    }
-
-    const Child = {
-      inheritAttrs: false,
-      setup(_: any, { attrs }: any) {
-        return () => {
-          return h(Wrapper, {
-            scopedSlots: {
-              default: () => h('div', { attrs })
-            }
-          })
-        }
-      }
-    }
-
-    const Parent = {
-      render: () =>
-        h(Child, { attrs: toggle.value ? { id: 'foo' } : { class: 'baz' } })
-    }
-
-    const vm = new Vue(Parent).$mount()
-    expect(vm.$el.outerHTML).toMatch(`<div id="foo"></div>`)
-
-    // should update even though it's not reactive
-    toggle.value = false
-    await nextTick()
-    expect(vm.$el.outerHTML).toMatch(`<div class="baz"></div>`)
-  })
-
-  it('context.slots', async () => {
-    const id = ref('foo')
-
-    const Child = {
-      setup(_props: any, { slots }: any) {
-        // #12672 behavior consistency with Vue 3: should be able to access
-        // slots directly in setup()
-        expect(slots.foo()).toBeTruthy()
-        return () => h('div', [...slots.foo(), ...slots.bar()])
-      }
-    }
-
-    const Parent = {
-      components: { Child },
-      setup() {
-        return { id }
-      },
-      template: `<Child>
-        <template #foo>{{ id }}</template>
-        <template #bar>bar</template>
-      </Child>`
-    }
-
-    const vm = new Vue(Parent).$mount()
-    expect(vm.$el.outerHTML).toMatch(`<div>foobar</div>`)
-
-    // should update even though it's not reactive
-    id.value = 'baz'
-    await nextTick()
-    expect(vm.$el.outerHTML).toMatch(`<div>bazbar</div>`)
-  })
-
-  it('context.emit', async () => {
-    const count = ref(0)
-    const spy = vi.fn()
-
-    const Child = {
-      props: {
-        count: {
-          type: Number,
-          default: 1
-        }
-      },
-      setup(props, { emit }) {
-        return () =>
-          h(
-            'div',
-            {
-              on: { click: () => emit('inc', props.count + 1) }
-            },
-            props.count
-          )
-      }
-    }
-
-    const Parent = {
-      components: { Child },
-      setup: () => ({
-        count,
-        onInc(newVal: number) {
-          spy()
-          count.value = newVal
-        }
-      }),
-      template: `<Child :count="count" @inc="onInc" />`
-    }
-
-    const vm = new Vue(Parent).$mount()
-    expect(vm.$el.outerHTML).toMatch(`<div>0</div>`)
-
-    // emit should trigger parent handler
-    triggerEvent(vm.$el as HTMLElement, 'click')
-    expect(spy).toHaveBeenCalled()
-    await nextTick()
-    expect(vm.$el.outerHTML).toMatch(`<div>1</div>`)
-  })
-
-  it('directive resolution', () => {
-    const spy = vi.fn()
-    new Vue({
-      setup: () => ({
-        __sfc: true,
-        vDir: {
-          inserted: spy
-        }
-      }),
-      template: `<div v-dir />`
-    }).$mount()
-    expect(spy).toHaveBeenCalled()
-  })
-
-  // #12743
-  it('directive resolution for shorthand', () => {
-    const spy = vi.fn()
-    new Vue({
-      setup: () => ({
-        __sfc: true,
-        vDir: spy
-      }),
-      template: `<div v-dir />`
-    }).$mount()
-    expect(spy).toHaveBeenCalled()
-  })
-  
-  // #12561
-  it('setup props should be reactive', () => {
-    const msg = ref('hi')
-
-    const Child = {
-      props: ['msg'],
-      setup: props => {
-        expect(isReactive(props)).toBe(true)
-        expect(isRef(toRef(props, 'foo'))).toBe(true)
-        return () => {}
-      }
-    }
-
-    new Vue({
-      setup() {
-        return h => h(Child, { props: { msg } })
-      }
-    }).$mount()
-  })
-
-  it('should not track dep accessed in setup', async () => {
-    const spy = vi.fn()
-    const msg = ref('hi')
-
-    const Child = {
-      setup: () => {
-        msg.value
-        return () => {}
-      }
-    }
-
-    new Vue({
-      setup() {
-        return h => {
-          spy()
-          return h(Child)
-        }
-      }
-    }).$mount()
-
-    expect(spy).toHaveBeenCalledTimes(1)
-
-    msg.value = 'bye'
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
-
-  it('context.listeners', async () => {
-    let _listeners
-    const Child = {
-      setup(_, { listeners }) {
-        _listeners = listeners
-        return () => {}
-      }
-    }
-
-    const Parent = {
-      data: () => ({ log: () => 1 }),
-      template: `<Child @foo="log" />`,
-      components: { Child }
-    }
-
-    const vm = new Vue(Parent).$mount()
-
-    expect(_listeners.foo()).toBe(1)
-    vm.log = () => 2
-    await nextTick()
-    expect(_listeners.foo()).toBe(2)
-  })
-})
diff --git a/test/unit/features/v3/apiWatch.spec.ts b/test/unit/features/v3/apiWatch.spec.ts
deleted file mode 100644
index a684d16116e..00000000000
--- a/test/unit/features/v3/apiWatch.spec.ts
+++ /dev/null
@@ -1,1234 +0,0 @@
-import Vue from 'vue'
-import {
-  watch,
-  watchEffect,
-  watchPostEffect,
-  watchSyncEffect,
-  reactive,
-  computed,
-  ref,
-  triggerRef,
-  shallowRef,
-  h,
-  onMounted,
-  getCurrentInstance,
-  effectScope,
-  TrackOpTypes,
-  TriggerOpTypes,
-  DebuggerEvent
-} from 'v3'
-import { nextTick } from 'core/util'
-import { set } from 'core/observer'
-import { Component } from 'types/component'
-
-// reference: https://vue-composition-api-rfc.netlify.com/api.html#watch
-
-describe('api: watch', () => {
-  it('effect', async () => {
-    const state = reactive({ count: 0 })
-    let dummy
-    watchEffect(() => {
-      dummy = state.count
-    })
-    expect(dummy).toBe(0)
-
-    state.count++
-    await nextTick()
-    expect(dummy).toBe(1)
-  })
-
-  it('watching single source: getter', async () => {
-    const state = reactive({ count: 0 })
-    let dummy
-    watch(
-      () => state.count,
-      (count, prevCount) => {
-        dummy = [count, prevCount]
-        // assert types
-        count + 1
-        if (prevCount) {
-          prevCount + 1
-        }
-      }
-    )
-    state.count++
-    await nextTick()
-    expect(dummy).toMatchObject([1, 0])
-  })
-
-  it('watching single source: ref', async () => {
-    const count = ref(0)
-    let dummy
-    watch(count, (count, prevCount) => {
-      dummy = [count, prevCount]
-      // assert types
-      count + 1
-      if (prevCount) {
-        prevCount + 1
-      }
-    })
-    count.value++
-    await nextTick()
-    expect(dummy).toMatchObject([1, 0])
-  })
-
-  it('watching single source: array', async () => {
-    const array = reactive({ a: [] as number[] }).a
-    const spy = vi.fn()
-    watch(array, spy)
-    array.push(1)
-    await nextTick()
-    expect(spy).toBeCalledTimes(1)
-    expect(spy).toBeCalledWith([1], expect.anything(), expect.anything())
-  })
-
-  it('should not fire if watched getter result did not change', async () => {
-    const spy = vi.fn()
-    const n = ref(0)
-    watch(() => n.value % 2, spy)
-
-    n.value++
-    await nextTick()
-    expect(spy).toBeCalledTimes(1)
-
-    n.value += 2
-    await nextTick()
-    // should not be called again because getter result did not change
-    expect(spy).toBeCalledTimes(1)
-  })
-
-  it('watching single source: computed ref', async () => {
-    const count = ref(0)
-    const plus = computed(() => count.value + 1)
-    let dummy
-    watch(plus, (count, prevCount) => {
-      dummy = [count, prevCount]
-      // assert types
-      count + 1
-      if (prevCount) {
-        prevCount + 1
-      }
-    })
-    count.value++
-    await nextTick()
-    expect(dummy).toMatchObject([2, 1])
-  })
-
-  it('watching primitive with deep: true', async () => {
-    const count = ref(0)
-    let dummy
-    watch(
-      count,
-      (c, prevCount) => {
-        dummy = [c, prevCount]
-      },
-      {
-        deep: true
-      }
-    )
-    count.value++
-    await nextTick()
-    expect(dummy).toMatchObject([1, 0])
-  })
-
-  it('directly watching reactive object (with automatic deep: true)', async () => {
-    const src = reactive({
-      count: 0
-    })
-    let dummy
-    watch(src, ({ count }) => {
-      dummy = count
-    })
-    src.count++
-    await nextTick()
-    expect(dummy).toBe(1)
-  })
-
-  it('deep watch w/ raw refs', async () => {
-    const count = ref(0)
-    const src = reactive({
-      arr: [count]
-    })
-    let dummy
-    watch(src, ({ arr: [{ value }] }) => {
-      dummy = value
-    })
-    count.value++
-    await nextTick()
-    expect(dummy).toBe(1)
-  })
-
-  it('watching multiple sources', async () => {
-    const state = reactive({ count: 1 })
-    const count = ref(1)
-    const plus = computed(() => count.value + 1)
-
-    let dummy
-    watch([() => state.count, count, plus], (vals, oldVals) => {
-      dummy = [vals, oldVals]
-      // assert types
-      vals.concat(1)
-      oldVals.concat(1)
-    })
-
-    state.count++
-    count.value++
-    await nextTick()
-    expect(dummy).toMatchObject([
-      [2, 2, 3],
-      [1, 1, 2]
-    ])
-  })
-
-  it('watching multiple sources: readonly array', async () => {
-    const state = reactive({ count: 1 })
-    const status = ref(false)
-
-    let dummy
-    watch([() => state.count, status] as const, (vals, oldVals) => {
-      dummy = [vals, oldVals]
-      const [count] = vals
-      const [, oldStatus] = oldVals
-      // assert types
-      count + 1
-      oldStatus === true
-    })
-
-    state.count++
-    status.value = true
-    await nextTick()
-    expect(dummy).toMatchObject([
-      [2, true],
-      [1, false]
-    ])
-  })
-
-  it('watching multiple sources: reactive object (with automatic deep: true)', async () => {
-    const src = reactive({ count: 0 })
-    let dummy
-    watch([src], ([state]) => {
-      dummy = state
-      // assert types
-      state.count === 1
-    })
-    src.count++
-    await nextTick()
-    expect(dummy).toMatchObject({ count: 1 })
-  })
-
-  it('warn invalid watch source', () => {
-    // @ts-expect-error
-    watch(1, () => {})
-    expect(`Invalid watch source`).toHaveBeenWarned()
-  })
-
-  it('warn invalid watch source: multiple sources', () => {
-    watch([1], () => {})
-    expect(`Invalid watch source`).toHaveBeenWarned()
-  })
-
-  it('stopping the watcher (effect)', async () => {
-    const state = reactive({ count: 0 })
-    let dummy
-    const stop = watchEffect(() => {
-      dummy = state.count
-    })
-    expect(dummy).toBe(0)
-
-    stop()
-    state.count++
-    await nextTick()
-    // should not update
-    expect(dummy).toBe(0)
-  })
-
-  it('stopping the watcher (with source)', async () => {
-    const state = reactive({ count: 0 })
-    let dummy
-    const stop = watch(
-      () => state.count,
-      count => {
-        dummy = count
-      }
-    )
-
-    state.count++
-    await nextTick()
-    expect(dummy).toBe(1)
-
-    stop()
-    state.count++
-    await nextTick()
-    // should not update
-    expect(dummy).toBe(1)
-  })
-
-  it('cleanup registration (effect)', async () => {
-    const state = reactive({ count: 0 })
-    const cleanup = vi.fn()
-    let dummy
-    const stop = watchEffect(onCleanup => {
-      onCleanup(cleanup)
-      dummy = state.count
-    })
-    expect(dummy).toBe(0)
-
-    state.count++
-    await nextTick()
-    expect(cleanup).toHaveBeenCalledTimes(1)
-    expect(dummy).toBe(1)
-
-    stop()
-    expect(cleanup).toHaveBeenCalledTimes(2)
-  })
-
-  it('cleanup registration (with source)', async () => {
-    const count = ref(0)
-    const cleanup = vi.fn()
-    let dummy
-    const stop = watch(count, (count, prevCount, onCleanup) => {
-      onCleanup(cleanup)
-      dummy = count
-    })
-
-    count.value++
-    await nextTick()
-    expect(cleanup).toHaveBeenCalledTimes(0)
-    expect(dummy).toBe(1)
-
-    count.value++
-    await nextTick()
-    expect(cleanup).toHaveBeenCalledTimes(1)
-    expect(dummy).toBe(2)
-
-    stop()
-    expect(cleanup).toHaveBeenCalledTimes(2)
-  })
-
-  it('flush timing: pre (default)', async () => {
-    const count = ref(0)
-    const count2 = ref(0)
-
-    let callCount = 0
-    let result1
-    let result2
-    const assertion = vi.fn((count, count2Value) => {
-      callCount++
-      // on mount, the watcher callback should be called before DOM render
-      // on update, should be called before the count is updated
-      const expectedDOM =
-        callCount === 1 ? `<div></div>` : `<div>${count - 1}</div>`
-      result1 = container.innerHTML === expectedDOM
-
-      // in a pre-flush callback, all state should have been updated
-      const expectedState = callCount - 1
-      result2 = count === expectedState && count2Value === expectedState
-    })
-
-    const Comp = {
-      setup() {
-        watchEffect(() => {
-          assertion(count.value, count2.value)
-        })
-        return () => h('div', count.value)
-      }
-    }
-    const container = document.createElement('div')
-    const root = document.createElement('div')
-    container.appendChild(root)
-    new Vue(Comp).$mount(root)
-    expect(assertion).toHaveBeenCalledTimes(1)
-    expect(result1).toBe(true)
-    expect(result2).toBe(true)
-
-    count.value++
-    count2.value++
-    await nextTick()
-    // two mutations should result in 1 callback execution
-    expect(assertion).toHaveBeenCalledTimes(2)
-    expect(result1).toBe(true)
-    expect(result2).toBe(true)
-  })
-
-  // #12569
-  it('flush:pre watcher triggered before component mount (in child components)', () => {
-    const count = ref(0)
-    const spy = vi.fn()
-    const Comp = {
-      setup() {
-        watch(count, spy)
-        count.value++
-        return h => h('div')
-      }
-    }
-    new Vue({
-      render: h => h(Comp)
-    }).$mount()
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
-
-  it('flush timing: post', async () => {
-    const count = ref(0)
-    let result
-    const assertion = vi.fn(count => {
-      result = container.innerHTML === `<div>${count}</div>`
-    })
-
-    const Comp = {
-      setup() {
-        watchEffect(
-          () => {
-            assertion(count.value)
-          },
-          { flush: 'post' }
-        )
-        return () => h('div', count.value)
-      }
-    }
-    const container = document.createElement('div')
-    const root = document.createElement('div')
-    container.appendChild(root)
-    new Vue(Comp).$mount(root)
-    expect(assertion).toHaveBeenCalledTimes(1)
-    expect(result).toBe(true)
-
-    count.value++
-    await nextTick()
-    expect(assertion).toHaveBeenCalledTimes(2)
-    expect(result).toBe(true)
-  })
-
-  it('watchPostEffect', async () => {
-    const count = ref(0)
-    let result
-    const assertion = vi.fn(count => {
-      result = container.innerHTML === `<div>${count}</div>`
-    })
-
-    const Comp = {
-      setup() {
-        watchPostEffect(() => {
-          assertion(count.value)
-        })
-        return () => h('div', count.value)
-      }
-    }
-    const container = document.createElement('div')
-    const root = document.createElement('div')
-    container.appendChild(root)
-    new Vue(Comp).$mount(root)
-    expect(assertion).toHaveBeenCalledTimes(1)
-    expect(result).toBe(true)
-
-    count.value++
-    await nextTick()
-    expect(assertion).toHaveBeenCalledTimes(2)
-    expect(result).toBe(true)
-  })
-
-  it('flush timing: sync', async () => {
-    const count = ref(0)
-    const count2 = ref(0)
-
-    let callCount = 0
-    let result1
-    let result2
-    const assertion = vi.fn(count => {
-      callCount++
-      // on mount, the watcher callback should be called before DOM render
-      // on update, should be called before the count is updated
-      const expectedDOM =
-        callCount === 1 ? `<div></div>` : `<div>${count - 1}</div>`
-      result1 = container.innerHTML === expectedDOM
-
-      // in a sync callback, state mutation on the next line should not have
-      // executed yet on the 2nd call, but will be on the 3rd call.
-      const expectedState = callCount < 3 ? 0 : 1
-      result2 = count2.value === expectedState
-    })
-
-    const Comp = {
-      setup() {
-        watchEffect(
-          () => {
-            assertion(count.value)
-          },
-          {
-            flush: 'sync'
-          }
-        )
-        return () => h('div', count.value)
-      }
-    }
-    const container = document.createElement('div')
-    const root = document.createElement('div')
-    container.appendChild(root)
-    new Vue(Comp).$mount(root)
-    expect(assertion).toHaveBeenCalledTimes(1)
-    expect(result1).toBe(true)
-    expect(result2).toBe(true)
-
-    count.value++
-    count2.value++
-    await nextTick()
-    expect(assertion).toHaveBeenCalledTimes(3)
-    expect(result1).toBe(true)
-    expect(result2).toBe(true)
-  })
-
-  it('watchSyncEffect', async () => {
-    const count = ref(0)
-    const count2 = ref(0)
-
-    let callCount = 0
-    let result1
-    let result2
-    const assertion = vi.fn(count => {
-      callCount++
-      // on mount, the watcher callback should be called before DOM render
-      // on update, should be called before the count is updated
-      const expectedDOM =
-        callCount === 1 ? `<div></div>` : `<div>${count - 1}</div>`
-      result1 = container.innerHTML === expectedDOM
-
-      // in a sync callback, state mutation on the next line should not have
-      // executed yet on the 2nd call, but will be on the 3rd call.
-      const expectedState = callCount < 3 ? 0 : 1
-      result2 = count2.value === expectedState
-    })
-
-    const Comp = {
-      setup() {
-        watchSyncEffect(() => {
-          assertion(count.value)
-        })
-        return () => h('div', count.value)
-      }
-    }
-    const container = document.createElement('div')
-    const root = document.createElement('div')
-    container.appendChild(root)
-    new Vue(Comp).$mount(root)
-    expect(assertion).toHaveBeenCalledTimes(1)
-    expect(result1).toBe(true)
-    expect(result2).toBe(true)
-
-    count.value++
-    count2.value++
-    await nextTick()
-    expect(assertion).toHaveBeenCalledTimes(3)
-    expect(result1).toBe(true)
-    expect(result2).toBe(true)
-  })
-
-  it('should not fire on component unmount w/ flush: post', async () => {
-    const toggle = ref(true)
-    const cb = vi.fn()
-    const Comp = {
-      setup() {
-        watch(toggle, cb, { flush: 'post' })
-      },
-      render() {}
-    }
-    const App = {
-      render() {
-        return toggle.value ? h(Comp) : null
-      }
-    }
-    new Vue(App).$mount()
-    expect(cb).not.toHaveBeenCalled()
-    toggle.value = false
-    await nextTick()
-    expect(cb).not.toHaveBeenCalled()
-  })
-
-  it('should not fire on component unmount w/ flush: pre', async () => {
-    const toggle = ref(true)
-    const cb = vi.fn()
-    const Comp = {
-      setup() {
-        watch(toggle, cb, { flush: 'pre' })
-      },
-      render() {}
-    }
-    const App = {
-      render() {
-        return toggle.value ? h(Comp) : null
-      }
-    }
-    new Vue(App).$mount()
-    expect(cb).not.toHaveBeenCalled()
-    toggle.value = false
-    await nextTick()
-    expect(cb).not.toHaveBeenCalled()
-  })
-
-  // vuejs/core#1763
-  it('flush: pre watcher watching props should fire before child update', async () => {
-    const a = ref(0)
-    const b = ref(0)
-    const c = ref(0)
-    const calls: string[] = []
-
-    const Comp = {
-      props: ['a', 'b'],
-      setup(props: any) {
-        watch(
-          () => props.a + props.b,
-          () => {
-            calls.push('watcher 1')
-            c.value++
-          },
-          { flush: 'pre' }
-        )
-
-        // vuejs/core#1777 chained pre-watcher
-        watch(
-          c,
-          () => {
-            calls.push('watcher 2')
-          },
-          { flush: 'pre' }
-        )
-        return () => {
-          c.value
-          calls.push('render')
-        }
-      }
-    }
-
-    const App = {
-      render() {
-        return h(Comp, { props: { a: a.value, b: b.value } })
-      }
-    }
-
-    new Vue(App).$mount()
-    expect(calls).toEqual(['render'])
-
-    // both props are updated
-    // should trigger pre-flush watcher first and only once
-    // then trigger child render
-    a.value++
-    b.value++
-    await nextTick()
-    expect(calls).toEqual(['render', 'watcher 1', 'watcher 2', 'render'])
-  })
-
-  // vuejs/core#5721
-  it('flush: pre triggered in component setup should be buffered and called before mounted', () => {
-    const count = ref(0)
-    const calls: string[] = []
-    const App = {
-      render() {},
-      setup() {
-        watch(
-          count,
-          () => {
-            calls.push('watch ' + count.value)
-          },
-          { flush: 'pre' }
-        )
-        onMounted(() => {
-          calls.push('mounted')
-        })
-        // mutate multiple times
-        count.value++
-        count.value++
-        count.value++
-      }
-    }
-    new Vue(App).$mount()
-    expect(calls).toMatchObject(['watch 3', 'mounted'])
-  })
-
-  // vuejs/core#1852
-  it('flush: post watcher should fire after template refs updated', async () => {
-    const toggle = ref(false)
-    let dom: HTMLElement | null = null
-
-    const App = {
-      setup() {
-        const domRef = ref<any>(null)
-
-        watch(
-          toggle,
-          () => {
-            dom = domRef.value
-          },
-          { flush: 'post' }
-        )
-
-        return () => {
-          return toggle.value ? h('p', { ref: domRef }) : null
-        }
-      }
-    }
-
-    new Vue(App).$mount()
-    expect(dom).toBe(null)
-
-    toggle.value = true
-    await nextTick()
-    expect(dom!.tagName).toBe('P')
-  })
-
-  it('deep', async () => {
-    const state = reactive({
-      nested: {
-        count: ref(0)
-      },
-      array: [1, 2, 3]
-      // map: new Map([
-      //   ['a', 1],
-      //   ['b', 2]
-      // ]),
-      // set: new Set([1, 2, 3])
-    })
-
-    let dummy
-    watch(
-      () => state,
-      state => {
-        dummy = [
-          state.nested.count,
-          state.array[0]
-          // state.map.get('a'),
-          // state.set.has(1)
-        ]
-      },
-      { deep: true }
-    )
-
-    state.nested.count++
-    await nextTick()
-    expect(dummy).toEqual([1, 1])
-
-    // nested array mutation
-    set(state.array, 0, 2)
-    await nextTick()
-    expect(dummy).toEqual([1, 2])
-
-    // nested map mutation
-    // state.map.set('a', 2)
-    // await nextTick()
-    // expect(dummy).toEqual([1, 2, 2, true])
-
-    // nested set mutation
-    // state.set.delete(1)
-    // await nextTick()
-    // expect(dummy).toEqual([1, 2, 2, false])
-  })
-
-  it('watching deep ref', async () => {
-    const count = ref(0)
-    const double = computed(() => count.value * 2)
-    const state = reactive({ count, double })
-
-    let dummy
-    watch(
-      () => state,
-      state => {
-        dummy = [state.count, state.double]
-      },
-      { deep: true }
-    )
-
-    count.value++
-    await nextTick()
-    expect(dummy).toEqual([1, 2])
-  })
-
-  it('immediate', async () => {
-    const count = ref(0)
-    const cb = vi.fn()
-    watch(count, cb, { immediate: true })
-    expect(cb).toHaveBeenCalledTimes(1)
-    count.value++
-    await nextTick()
-    expect(cb).toHaveBeenCalledTimes(2)
-  })
-
-  it('immediate: triggers when initial value is null', async () => {
-    const state = ref(null)
-    const spy = vi.fn()
-    watch(() => state.value, spy, { immediate: true })
-    expect(spy).toHaveBeenCalled()
-  })
-
-  it('immediate: triggers when initial value is undefined', async () => {
-    const state = ref()
-    const spy = vi.fn()
-    watch(() => state.value, spy, { immediate: true })
-    expect(spy).toHaveBeenCalled()
-    state.value = 3
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(2)
-    // testing if undefined can trigger the watcher
-    state.value = undefined
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(3)
-    // it shouldn't trigger if the same value is set
-    state.value = undefined
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(3)
-  })
-
-  it('warn immediate option when using effect', async () => {
-    const count = ref(0)
-    let dummy
-    watchEffect(
-      () => {
-        dummy = count.value
-      },
-      // @ts-expect-error
-      { immediate: false }
-    )
-    expect(dummy).toBe(0)
-    expect(`"immediate" option is only respected`).toHaveBeenWarned()
-
-    count.value++
-    await nextTick()
-    expect(dummy).toBe(1)
-  })
-
-  it('warn and not respect deep option when using effect', async () => {
-    const arr = ref([1, [2]])
-    const spy = vi.fn()
-    watchEffect(
-      () => {
-        spy()
-        return arr
-      },
-      // @ts-expect-error
-      { deep: true }
-    )
-    expect(spy).toHaveBeenCalledTimes(1)
-    ;(arr.value[1] as Array<number>)[0] = 3
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(1)
-    expect(`"deep" option is only respected`).toHaveBeenWarned()
-  })
-
-  it('onTrack', async () => {
-    const events: DebuggerEvent[] = []
-    let dummy
-    const onTrack = vi.fn((e: DebuggerEvent) => {
-      events.push(e)
-    })
-    const obj = reactive({ foo: 1 })
-    const r = ref(2)
-    const c = computed(() => r.value + 1)
-    watchEffect(
-      () => {
-        dummy = obj.foo + r.value + c.value
-      },
-      { onTrack }
-    )
-    await nextTick()
-    expect(dummy).toEqual(6)
-    expect(onTrack).toHaveBeenCalledTimes(3)
-    expect(events).toMatchObject([
-      {
-        target: obj,
-        type: TrackOpTypes.GET,
-        key: 'foo'
-      },
-      {
-        target: r,
-        type: TrackOpTypes.GET,
-        key: 'value'
-      },
-      {
-        target: c,
-        type: TrackOpTypes.GET,
-        key: 'value'
-      }
-    ])
-  })
-
-  it('onTrigger', async () => {
-    const events: DebuggerEvent[] = []
-    let dummy
-    const onTrigger = vi.fn((e: DebuggerEvent) => {
-      events.push(e)
-    })
-    const obj = reactive<{
-      foo: number
-      bar: any[]
-      baz: { qux?: number }
-    }>({ foo: 1, bar: [], baz: {} })
-
-    watchEffect(
-      () => {
-        dummy = obj.foo + (obj.bar[0] || 0) + (obj.baz.qux || 0)
-      },
-      { onTrigger }
-    )
-    await nextTick()
-    expect(dummy).toBe(1)
-
-    obj.foo++
-    await nextTick()
-    expect(dummy).toBe(2)
-    expect(onTrigger).toHaveBeenCalledTimes(1)
-    expect(events[0]).toMatchObject({
-      type: TriggerOpTypes.SET,
-      key: 'foo',
-      target: obj,
-      oldValue: 1,
-      newValue: 2
-    })
-
-    obj.bar.push(1)
-    await nextTick()
-    expect(dummy).toBe(3)
-    expect(onTrigger).toHaveBeenCalledTimes(2)
-    expect(events[1]).toMatchObject({
-      type: TriggerOpTypes.ARRAY_MUTATION,
-      target: obj.bar,
-      key: 'push'
-    })
-
-    set(obj.baz, 'qux', 1)
-    await nextTick()
-    expect(dummy).toBe(4)
-    expect(onTrigger).toHaveBeenCalledTimes(3)
-    expect(events[2]).toMatchObject({
-      type: TriggerOpTypes.ADD,
-      target: obj.baz,
-      key: 'qux'
-    })
-  })
-
-  it('should work sync', () => {
-    const v = ref(1)
-    let calls = 0
-
-    watch(
-      v,
-      () => {
-        ++calls
-      },
-      {
-        flush: 'sync'
-      }
-    )
-
-    expect(calls).toBe(0)
-    v.value++
-    expect(calls).toBe(1)
-  })
-
-  test('should force trigger on triggerRef when watching a shallow ref', async () => {
-    const v = shallowRef({ a: 1 })
-    let sideEffect = 0
-    watch(v, obj => {
-      sideEffect = obj.a
-    })
-
-    v.value = v.value
-    await nextTick()
-    // should not trigger
-    expect(sideEffect).toBe(0)
-
-    v.value.a++
-    await nextTick()
-    // should not trigger
-    expect(sideEffect).toBe(0)
-
-    triggerRef(v)
-    await nextTick()
-    // should trigger now
-    expect(sideEffect).toBe(2)
-  })
-
-  test('should force trigger on triggerRef when watching multiple sources: shallow ref array', async () => {
-    const v = shallowRef([] as any)
-    const spy = vi.fn()
-    watch([v], () => {
-      spy()
-    })
-
-    v.value.push(1)
-    triggerRef(v)
-
-    await nextTick()
-    // should trigger now
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
-
-  // vuejs/core#2125
-  test('watchEffect should not recursively trigger itself', async () => {
-    const spy = vi.fn()
-    const price = ref(10)
-    const history = ref<number[]>([])
-    watchEffect(() => {
-      history.value.push(price.value)
-      spy()
-    })
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
-
-  // vuejs/core#2231
-  test('computed refs should not trigger watch if value has no change', async () => {
-    const spy = vi.fn()
-    const source = ref(0)
-    const price = computed(() => source.value === 0)
-    watch(price, spy)
-    source.value++
-    await nextTick()
-    source.value++
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
-
-  test('this.$watch should pass `this.proxy` to watch source as the first argument ', () => {
-    let instance: any
-    const source = vi.fn()
-
-    const Comp = {
-      render() {},
-      created(this: any) {
-        instance = this
-        this.$watch(source, function () {})
-      }
-    }
-
-    const root = document.createElement('div')
-    new Vue(Comp).$mount(root)
-
-    expect(instance).toBeDefined()
-    expect(source).toHaveBeenCalledWith(instance)
-  })
-
-  test('should not leak `this.proxy` to setup()', () => {
-    const source = vi.fn()
-
-    const Comp = {
-      render() {},
-      setup() {
-        watch(source, () => {})
-      }
-    }
-
-    const root = document.createElement('div')
-    new Vue(Comp).$mount(root)
-    // should not have any arguments
-    expect(source.mock.calls[0]).toMatchObject([])
-  })
-
-  // vuejs/core#2728
-  test('pre watcher callbacks should not track dependencies', async () => {
-    const a = ref(0)
-    const b = ref(0)
-    const updated = vi.fn()
-    const cb = vi.fn()
-
-    const Child = {
-      props: ['a'],
-      updated,
-      watch: {
-        a() {
-          cb()
-          b.value
-        }
-      },
-      render() {
-        return h('div', this.a)
-      }
-    }
-
-    const Parent = {
-      render() {
-        return h(Child, { props: { a: a.value } })
-      }
-    }
-
-    const root = document.createElement('div')
-    new Vue(Parent).$mount(root)
-
-    a.value++
-    await nextTick()
-    expect(updated).toHaveBeenCalledTimes(1)
-    expect(cb).toHaveBeenCalledTimes(1)
-
-    b.value++
-    await nextTick()
-    // should not track b as dependency of Child
-    expect(updated).toHaveBeenCalledTimes(1)
-    expect(cb).toHaveBeenCalledTimes(1)
-  })
-
-  it('watching sources: ref<any[]>', async () => {
-    const foo = ref([1])
-    const spy = vi.fn()
-    watch(foo, () => {
-      spy()
-    })
-    foo.value = foo.value.slice()
-    await nextTick()
-    expect(spy).toBeCalledTimes(1)
-  })
-
-  it('watching multiple sources: computed', async () => {
-    let count = 0
-    const value = ref('1')
-    const plus = computed(() => !!value.value)
-    watch([plus], () => {
-      count++
-    })
-    value.value = '2'
-    await nextTick()
-    expect(plus.value).toBe(true)
-    expect(count).toBe(0)
-  })
-
-  // vuejs/core#4158
-  test('watch should not register in owner component if created inside detached scope', () => {
-    let instance: Component
-    const Comp = {
-      setup() {
-        instance = getCurrentInstance()!.proxy
-        effectScope(true).run(() => {
-          watch(
-            () => 1,
-            () => {}
-          )
-        })
-        return () => ''
-      }
-    }
-    const root = document.createElement('div')
-    new Vue(Comp).$mount(root)
-    // should not record watcher in detached scope and only the instance's
-    // own update effect
-    expect(instance!._scope.effects.length).toBe(1)
-  })
-
-  // #12578
-  test('template ref triggered watcher should fire after component mount', async () => {
-    const order: string[] = []
-    const Child = { template: '<div/>' }
-    const App = {
-      setup() {
-        const child = ref<any>(null)
-        onMounted(() => {
-          order.push('mounted')
-        })
-        watch(child, () => {
-          order.push('watcher')
-        })
-        return { child }
-      },
-      components: { Child },
-      template: `<Child ref="child"/>`
-    }
-    new Vue(App).$mount()
-
-    await nextTick()
-    expect(order).toMatchObject([`mounted`, `watcher`])
-  })
-
-  // #12624
-  test('pre watch triggered in mounted hook', async () => {
-    const spy = vi.fn()
-    new Vue({
-      setup() {
-        const c = ref(0)
-
-        onMounted(() => {
-          c.value++
-        })
-
-        watchEffect(() => spy(c.value))
-        return () => {}
-      }
-    }).$mount()
-    expect(spy).toHaveBeenCalledTimes(1)
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(2)
-  })
-
-  // #12643
-  test('should trigger watch on reactive object when new property is added via set()', () => {
-    const spy = vi.fn()
-    const obj = reactive({})
-    watch(obj, spy, { flush: 'sync' })
-    set(obj, 'foo', 1)
-    expect(spy).toHaveBeenCalled()
-  })
-
-  test('should not trigger watch when calling set() on ref value', () => {
-    const spy = vi.fn()
-    const r = ref({})
-    watch(r, spy, { flush: 'sync' })
-    set(r.value, 'foo', 1)
-    expect(spy).not.toHaveBeenCalled()
-  })
-
-  // #12664
-  it('queueing multiple flush: post watchers', async () => {
-    const parentSpy = vi.fn()
-    const childSpy = vi.fn()
-
-    const Child = {
-      setup() {
-        const el = ref()
-        watch(el, childSpy, { flush: 'post' })
-        return { el }
-      },
-      template: `<div><span ref="el">hello child</span></div>`
-    }
-    const App = {
-      components: { Child },
-      setup() {
-        const el = ref()
-        watch(el, parentSpy, { flush: 'post' })
-        return { el }
-      },
-      template: `<div><Child /><span ref="el">hello app1</span></div>`
-    }
-
-    const container = document.createElement('div')
-    const root = document.createElement('div')
-    container.appendChild(root)
-    new Vue(App).$mount(root)
-
-    await nextTick()
-    expect(parentSpy).toHaveBeenCalledTimes(1)
-    expect(childSpy).toHaveBeenCalledTimes(1)
-  })
-
-  // #12967
-  test('trigger when adding new property with Vue.set (getter)', async () => {
-    const spy = vi.fn()
-    const r = reactive({ exist: 5 })
-    watch(() => r, spy, { deep: true })
-    set(r, 'add', 1)
-
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
-
-  test('trigger when adding new property with Vue.set (getter in array source)', async () => {
-    const spy = vi.fn()
-    const r = reactive({ exist: 5 })
-    watch([() => r], spy, { deep: true })
-    set(r, 'add', 1)
-
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
-
-  test('trigger when adding new property with Vue.set (reactive in array source)', async () => {
-    const spy = vi.fn()
-    const r = reactive({ exist: 5 })
-    watch([r], spy, { deep: true })
-    set(r, 'add', 1)
-
-    await nextTick()
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
-})
diff --git a/test/unit/features/v3/reactivity/computed.spec.ts b/test/unit/features/v3/reactivity/computed.spec.ts
deleted file mode 100644
index b03278877cf..00000000000
--- a/test/unit/features/v3/reactivity/computed.spec.ts
+++ /dev/null
@@ -1,301 +0,0 @@
-import {
-  computed,
-  reactive,
-  ref,
-  isReadonly,
-  WritableComputedRef,
-  DebuggerEvent,
-  TrackOpTypes,
-  TriggerOpTypes
-} from 'v3'
-import { effect } from 'v3/reactivity/effect'
-import { nextTick } from 'core/util'
-import { set, del } from 'core/observer/index'
-
-describe('reactivity/computed', () => {
-  it('should return updated value', () => {
-    const value = reactive({ foo: 1 })
-    const cValue = computed(() => value.foo)
-    expect(cValue.value).toBe(1)
-    value.foo = 2
-    expect(cValue.value).toBe(2)
-  })
-
-  it('should compute lazily', () => {
-    const value = reactive<{ foo?: number }>({ foo: undefined })
-    const getter = vi.fn(() => value.foo)
-    const cValue = computed(getter)
-
-    // lazy
-    expect(getter).not.toHaveBeenCalled()
-
-    expect(cValue.value).toBe(undefined)
-    expect(getter).toHaveBeenCalledTimes(1)
-
-    // should not compute again
-    cValue.value
-    expect(getter).toHaveBeenCalledTimes(1)
-
-    // should not compute until needed
-    value.foo = 1
-    expect(getter).toHaveBeenCalledTimes(1)
-
-    // now it should compute
-    expect(cValue.value).toBe(1)
-    expect(getter).toHaveBeenCalledTimes(2)
-
-    // should not compute again
-    cValue.value
-    expect(getter).toHaveBeenCalledTimes(2)
-  })
-
-  it('should trigger effect', () => {
-    const value = reactive<{ foo?: number }>({ foo: undefined })
-    const cValue = computed(() => value.foo)
-    let dummy
-    effect(() => {
-      dummy = cValue.value
-    })
-    expect(dummy).toBe(undefined)
-    value.foo = 1
-    expect(dummy).toBe(1)
-  })
-
-  it('should work when chained', () => {
-    const value = reactive({ foo: 0 })
-    const c1 = computed(() => value.foo)
-    const c2 = computed(() => c1.value + 1)
-    expect(c2.value).toBe(1)
-    expect(c1.value).toBe(0)
-    value.foo++
-    expect(c2.value).toBe(2)
-    expect(c1.value).toBe(1)
-  })
-
-  it('should trigger effect when chained', () => {
-    const value = reactive({ foo: 0 })
-    const getter1 = vi.fn(() => value.foo)
-    const getter2 = vi.fn(() => {
-      return c1.value + 1
-    })
-    const c1 = computed(getter1)
-    const c2 = computed(getter2)
-
-    let dummy
-    effect(() => {
-      dummy = c2.value
-    })
-    expect(dummy).toBe(1)
-    expect(getter1).toHaveBeenCalledTimes(1)
-    expect(getter2).toHaveBeenCalledTimes(1)
-    value.foo++
-    expect(dummy).toBe(2)
-    // should not result in duplicate calls
-    expect(getter1).toHaveBeenCalledTimes(2)
-    expect(getter2).toHaveBeenCalledTimes(2)
-  })
-
-  it('should trigger effect when chained (mixed invocations)', async () => {
-    const value = reactive({ foo: 0 })
-    const getter1 = vi.fn(() => value.foo)
-    const getter2 = vi.fn(() => {
-      return c1.value + 1
-    })
-    const c1 = computed(getter1)
-    const c2 = computed(getter2)
-
-    let dummy
-    // @discrepancy Vue 2 chained computed doesn't work with sync watchers
-    effect(() => {
-      dummy = c1.value + c2.value
-    }, nextTick)
-    expect(dummy).toBe(1)
-
-    expect(getter1).toHaveBeenCalledTimes(1)
-    expect(getter2).toHaveBeenCalledTimes(1)
-    value.foo++
-
-    await nextTick()
-    expect(dummy).toBe(3)
-    // should not result in duplicate calls
-    expect(getter1).toHaveBeenCalledTimes(2)
-    expect(getter2).toHaveBeenCalledTimes(2)
-  })
-
-  it('should no longer update when stopped', () => {
-    const value = reactive<{ foo?: number }>({ foo: undefined })
-    const cValue = computed(() => value.foo)
-    let dummy
-    effect(() => {
-      dummy = cValue.value
-    })
-    expect(dummy).toBe(undefined)
-    value.foo = 1
-    expect(dummy).toBe(1)
-    cValue.effect.teardown()
-    value.foo = 2
-    expect(dummy).toBe(1)
-  })
-
-  it('should support setter', () => {
-    const n = ref(1)
-    const plusOne = computed({
-      get: () => n.value + 1,
-      set: val => {
-        n.value = val - 1
-      }
-    })
-
-    expect(plusOne.value).toBe(2)
-    n.value++
-    expect(plusOne.value).toBe(3)
-
-    plusOne.value = 0
-    expect(n.value).toBe(-1)
-  })
-
-  it('should trigger effect w/ setter', () => {
-    const n = ref(1)
-    const plusOne = computed({
-      get: () => n.value + 1,
-      set: val => {
-        n.value = val - 1
-      }
-    })
-
-    let dummy
-    effect(() => {
-      dummy = n.value
-    })
-    expect(dummy).toBe(1)
-
-    plusOne.value = 0
-    expect(dummy).toBe(-1)
-  })
-
-  // #5720
-  it('should invalidate before non-computed effects', async () => {
-    let plusOneValues: number[] = []
-    const n = ref(0)
-    const plusOne = computed(() => n.value + 1)
-    effect(() => {
-      n.value
-      plusOneValues.push(plusOne.value)
-    }, nextTick)
-    expect(plusOneValues).toMatchObject([1])
-    // access plusOne, causing it to be non-dirty
-    plusOne.value
-    // mutate n
-    n.value++
-    await nextTick()
-    // on the 2nd run, plusOne.value should have already updated.
-    expect(plusOneValues).toMatchObject([1, 2])
-  })
-
-  it('should warn if trying to set a readonly computed', () => {
-    const n = ref(1)
-    const plusOne = computed(() => n.value + 1)
-    ;(plusOne as WritableComputedRef<number>).value++ // Type cast to prevent TS from preventing the error
-
-    expect(
-      'Write operation failed: computed value is readonly'
-    ).toHaveBeenWarnedLast()
-  })
-
-  it('should be readonly', () => {
-    let a = { a: 1 }
-    const x = computed(() => a)
-    expect(isReadonly(x)).toBe(true)
-    expect(isReadonly(x.value)).toBe(false)
-    expect(isReadonly(x.value.a)).toBe(false)
-    const z = computed<typeof a>({
-      get() {
-        return a
-      },
-      set(v) {
-        a = v
-      }
-    })
-    expect(isReadonly(z)).toBe(false)
-    expect(isReadonly(z.value.a)).toBe(false)
-  })
-
-  it('should expose value when stopped', () => {
-    const x = computed(() => 1)
-    x.effect.teardown()
-    expect(x.value).toBe(1)
-  })
-
-  it('debug: onTrack', () => {
-    let events: DebuggerEvent[] = []
-    const onTrack = vi.fn((e: DebuggerEvent) => {
-      events.push(e)
-    })
-    const obj = reactive({ foo: 1, bar: 2 })
-    const c = computed(() => obj.foo + obj.bar, {
-      onTrack
-    })
-    expect(c.value).toEqual(3)
-    expect(onTrack).toHaveBeenCalledTimes(2)
-    expect(events).toEqual([
-      {
-        effect: c.effect,
-        target: obj,
-        type: TrackOpTypes.GET,
-        key: 'foo'
-      },
-      {
-        effect: c.effect,
-        target: obj,
-        type: TrackOpTypes.GET,
-        key: 'bar'
-      }
-    ])
-  })
-
-  it('debug: onTrigger', () => {
-    let events: DebuggerEvent[] = []
-    const onTrigger = vi.fn((e: DebuggerEvent) => {
-      events.push(e)
-    })
-    const obj = reactive({ foo: 1, bar: { baz: 2 } })
-    const c = computed(() => obj.foo + (obj.bar.baz || 0), { onTrigger })
-
-    // computed won't trigger compute until accessed
-    c.value
-
-    obj.foo++
-    expect(c.value).toBe(4)
-    expect(onTrigger).toHaveBeenCalledTimes(1)
-    expect(events[0]).toEqual({
-      effect: c.effect,
-      target: obj,
-      type: TriggerOpTypes.SET,
-      key: 'foo',
-      oldValue: 1,
-      newValue: 2
-    })
-
-    del(obj.bar, 'baz')
-    expect(c.value).toBe(2)
-    expect(onTrigger).toHaveBeenCalledTimes(2)
-    expect(events[1]).toEqual({
-      effect: c.effect,
-      target: obj.bar,
-      type: TriggerOpTypes.DELETE,
-      key: 'baz'
-    })
-
-    set(obj.bar, 'baz', 1)
-    expect(c.value).toBe(3)
-    expect(onTrigger).toHaveBeenCalledTimes(3)
-    expect(events[2]).toEqual({
-      effect: c.effect,
-      target: obj.bar,
-      type: TriggerOpTypes.ADD,
-      key: 'baz',
-      oldValue: undefined,
-      newValue: 1
-    })
-  })
-})
diff --git a/test/unit/features/v3/reactivity/effectScope.spec.ts b/test/unit/features/v3/reactivity/effectScope.spec.ts
deleted file mode 100644
index 78966e42e4d..00000000000
--- a/test/unit/features/v3/reactivity/effectScope.spec.ts
+++ /dev/null
@@ -1,318 +0,0 @@
-import Vue from 'vue'
-import { nextTick } from 'core/util'
-import {
-  watch,
-  watchEffect,
-  reactive,
-  computed,
-  ref,
-  ComputedRef,
-  EffectScope,
-  onScopeDispose,
-  getCurrentScope
-} from 'v3/index'
-import { effect } from 'v3/reactivity/effect'
-
-describe('reactivity/effectScope', () => {
-  it('should run', () => {
-    const fnSpy = vi.fn(() => {})
-    new EffectScope().run(fnSpy)
-    expect(fnSpy).toHaveBeenCalledTimes(1)
-  })
-
-  it('should accept zero argument', () => {
-    const scope = new EffectScope()
-    expect(scope.effects.length).toBe(0)
-  })
-
-  it('should return run value', () => {
-    expect(new EffectScope().run(() => 1)).toBe(1)
-  })
-
-  it('should collect the effects', () => {
-    const scope = new EffectScope()
-    scope.run(() => {
-      let dummy
-      const counter = reactive({ num: 0 })
-      effect(() => (dummy = counter.num))
-
-      expect(dummy).toBe(0)
-      counter.num = 7
-      expect(dummy).toBe(7)
-    })
-
-    expect(scope.effects.length).toBe(1)
-  })
-
-  it('stop', () => {
-    let dummy, doubled
-    const counter = reactive({ num: 0 })
-
-    const scope = new EffectScope()
-    scope.run(() => {
-      effect(() => (dummy = counter.num))
-      effect(() => (doubled = counter.num * 2))
-    })
-
-    expect(scope.effects.length).toBe(2)
-
-    expect(dummy).toBe(0)
-    counter.num = 7
-    expect(dummy).toBe(7)
-    expect(doubled).toBe(14)
-
-    scope.stop()
-
-    counter.num = 6
-    expect(dummy).toBe(7)
-    expect(doubled).toBe(14)
-  })
-
-  it('should collect nested scope', () => {
-    let dummy, doubled
-    const counter = reactive({ num: 0 })
-
-    const scope = new EffectScope()
-    scope.run(() => {
-      effect(() => (dummy = counter.num))
-      // nested scope
-      new EffectScope().run(() => {
-        effect(() => (doubled = counter.num * 2))
-      })
-    })
-
-    expect(scope.effects.length).toBe(1)
-    expect(scope.scopes!.length).toBe(1)
-    expect(scope.scopes![0]).toBeInstanceOf(EffectScope)
-
-    expect(dummy).toBe(0)
-    counter.num = 7
-    expect(dummy).toBe(7)
-    expect(doubled).toBe(14)
-
-    // stop the nested scope as well
-    scope.stop()
-
-    counter.num = 6
-    expect(dummy).toBe(7)
-    expect(doubled).toBe(14)
-  })
-
-  it('nested scope can be escaped', () => {
-    let dummy, doubled
-    const counter = reactive({ num: 0 })
-
-    const scope = new EffectScope()
-    scope.run(() => {
-      effect(() => (dummy = counter.num))
-      // nested scope
-      new EffectScope(true).run(() => {
-        effect(() => (doubled = counter.num * 2))
-      })
-    })
-
-    expect(scope.effects.length).toBe(1)
-
-    expect(dummy).toBe(0)
-    counter.num = 7
-    expect(dummy).toBe(7)
-    expect(doubled).toBe(14)
-
-    scope.stop()
-
-    counter.num = 6
-    expect(dummy).toBe(7)
-
-    // nested scope should not be stopped
-    expect(doubled).toBe(12)
-  })
-
-  it('able to run the scope', () => {
-    let dummy, doubled
-    const counter = reactive({ num: 0 })
-
-    const scope = new EffectScope()
-    scope.run(() => {
-      effect(() => (dummy = counter.num))
-    })
-
-    expect(scope.effects.length).toBe(1)
-
-    scope.run(() => {
-      effect(() => (doubled = counter.num * 2))
-    })
-
-    expect(scope.effects.length).toBe(2)
-
-    counter.num = 7
-    expect(dummy).toBe(7)
-    expect(doubled).toBe(14)
-
-    scope.stop()
-  })
-
-  it('can not run an inactive scope', () => {
-    let dummy, doubled
-    const counter = reactive({ num: 0 })
-
-    const scope = new EffectScope()
-    scope.run(() => {
-      effect(() => (dummy = counter.num))
-    })
-
-    expect(scope.effects.length).toBe(1)
-
-    scope.stop()
-
-    scope.run(() => {
-      effect(() => (doubled = counter.num * 2))
-    })
-
-    expect('cannot run an inactive effect scope.').toHaveBeenWarned()
-
-    expect(scope.effects.length).toBe(1)
-
-    counter.num = 7
-    expect(dummy).toBe(0)
-    expect(doubled).toBe(undefined)
-  })
-
-  it('should fire onScopeDispose hook', () => {
-    let dummy = 0
-
-    const scope = new EffectScope()
-    scope.run(() => {
-      onScopeDispose(() => (dummy += 1))
-      onScopeDispose(() => (dummy += 2))
-    })
-
-    scope.run(() => {
-      onScopeDispose(() => (dummy += 4))
-    })
-
-    expect(dummy).toBe(0)
-
-    scope.stop()
-    expect(dummy).toBe(7)
-  })
-
-  it('should warn onScopeDispose() is called when there is no active effect scope', () => {
-    const spy = vi.fn()
-    const scope = new EffectScope()
-    scope.run(() => {
-      onScopeDispose(spy)
-    })
-
-    expect(spy).toHaveBeenCalledTimes(0)
-
-    onScopeDispose(spy)
-
-    expect(
-      'onScopeDispose() is called when there is no active effect scope to be associated with.'
-    ).toHaveBeenWarned()
-
-    scope.stop()
-    expect(spy).toHaveBeenCalledTimes(1)
-  })
-
-  it('should dereference child scope from parent scope after stopping child scope (no memleaks)', () => {
-    const parent = new EffectScope()
-    const child = parent.run(() => new EffectScope())!
-    expect(parent.scopes!.includes(child)).toBe(true)
-    child.stop()
-    expect(parent.scopes!.includes(child)).toBe(false)
-  })
-
-  it('test with higher level APIs', async () => {
-    const r = ref(1)
-
-    const computedSpy = vi.fn()
-    const watchSpy = vi.fn()
-    const watchEffectSpy = vi.fn()
-
-    let c: ComputedRef
-    const scope = new EffectScope()
-    scope.run(() => {
-      c = computed(() => {
-        computedSpy()
-        return r.value + 1
-      })
-
-      watch(r, watchSpy)
-      watchEffect(() => {
-        watchEffectSpy()
-        r.value
-      })
-    })
-
-    c!.value // computed is lazy so trigger collection
-    expect(computedSpy).toHaveBeenCalledTimes(1)
-    expect(watchSpy).toHaveBeenCalledTimes(0)
-    expect(watchEffectSpy).toHaveBeenCalledTimes(1)
-
-    r.value++
-    c!.value
-    await nextTick()
-    expect(computedSpy).toHaveBeenCalledTimes(2)
-    expect(watchSpy).toHaveBeenCalledTimes(1)
-    expect(watchEffectSpy).toHaveBeenCalledTimes(2)
-
-    scope.stop()
-
-    r.value++
-    c!.value
-    await nextTick()
-    // should not trigger anymore
-    expect(computedSpy).toHaveBeenCalledTimes(2)
-    expect(watchSpy).toHaveBeenCalledTimes(1)
-    expect(watchEffectSpy).toHaveBeenCalledTimes(2)
-  })
-
-  it('getCurrentScope() stays valid when running a detached nested EffectScope', () => {
-    const parentScope = new EffectScope()
-
-    parentScope.run(() => {
-      const currentScope = getCurrentScope()
-      expect(currentScope).toBeDefined()
-      const detachedScope = new EffectScope(true)
-      detachedScope.run(() => {})
-
-      expect(getCurrentScope()).toBe(currentScope)
-    })
-  })
-
-  it('calling .off() of a detached scope inside an active scope should not break currentScope', () => {
-    const parentScope = new EffectScope()
-
-    parentScope.run(() => {
-      const childScope = new EffectScope(true)
-      childScope.on()
-      childScope.off()
-      expect(getCurrentScope()).toBe(parentScope)
-    })
-  })
-
-  it('scope should not break currentScope when component call hooks', () => {
-    const scope = new EffectScope()
-    const vm = new Vue({
-      template: `
-          <div>
-            <div v-if="show" />
-          </div>
-        `,
-      data() {
-        return {
-          show: false
-        }
-      }
-    }).$mount()
-
-    scope.run(() => {
-      // call renderTriggered hook here
-      vm.show = true
-      // this effect should be collected by scope not the component scope
-      effect(() => {})
-    })
-    expect(scope.effects.length).toBe(1)
-  })
-})
diff --git a/test/unit/features/v3/reactivity/reactive.spec.ts b/test/unit/features/v3/reactivity/reactive.spec.ts
deleted file mode 100644
index aa49f103f6e..00000000000
--- a/test/unit/features/v3/reactivity/reactive.spec.ts
+++ /dev/null
@@ -1,312 +0,0 @@
-import { ref, isRef, reactive, isReactive, toRaw, markRaw, computed } from 'v3'
-import { set } from 'core/observer'
-import { effect } from 'v3/reactivity/effect'
-
-describe('reactivity/reactive', () => {
-  test('Object', () => {
-    const original = { foo: 1 }
-    const observed = reactive(original)
-    // @discrepancy Vue 2 does not create proxy objects
-    // expect(observed).not.toBe(original)
-    expect(isReactive(observed)).toBe(true)
-    // @discrepancy Vue 2 does not create proxy objects
-    // expect(isReactive(original)).toBe(false)
-    // get
-    expect(observed.foo).toBe(1)
-    // has
-    expect('foo' in observed).toBe(true)
-    // ownKeys
-    expect(Object.keys(observed)).toEqual(['foo'])
-  })
-
-  test('proto', () => {
-    const obj = {}
-    const reactiveObj = reactive(obj)
-    expect(isReactive(reactiveObj)).toBe(true)
-    // read prop of reactiveObject will cause reactiveObj[prop] to be reactive
-    // @ts-ignore
-    const prototype = reactiveObj['__proto__']
-    const otherObj = { data: ['a'] }
-    expect(isReactive(otherObj)).toBe(false)
-    const reactiveOther = reactive(otherObj)
-    expect(isReactive(reactiveOther)).toBe(true)
-    expect(reactiveOther.data[0]).toBe('a')
-  })
-
-  test('nested reactives', () => {
-    const original = {
-      nested: {
-        foo: 1
-      },
-      array: [{ bar: 2 }]
-    }
-    const observed = reactive(original)
-    expect(isReactive(observed.nested)).toBe(true)
-    expect(isReactive(observed.array)).toBe(true)
-    expect(isReactive(observed.array[0])).toBe(true)
-  })
-
-  // @discrepancy Vue 2 does not support collections
-  // test('observing subtypes of IterableCollections(Map, Set)', () => {
-  //   // subtypes of Map
-  //   class CustomMap extends Map {}
-  //   const cmap = reactive(new CustomMap())
-
-  //   expect(cmap instanceof Map).toBe(true)
-  //   expect(isReactive(cmap)).toBe(true)
-
-  //   cmap.set('key', {})
-  //   expect(isReactive(cmap.get('key'))).toBe(true)
-
-  //   // subtypes of Set
-  //   class CustomSet extends Set {}
-  //   const cset = reactive(new CustomSet())
-
-  //   expect(cset instanceof Set).toBe(true)
-  //   expect(isReactive(cset)).toBe(true)
-
-  //   let dummy
-  //   effect(() => (dummy = cset.has('value')))
-  //   expect(dummy).toBe(false)
-  //   cset.add('value')
-  //   expect(dummy).toBe(true)
-  //   cset.delete('value')
-  //   expect(dummy).toBe(false)
-  // })
-
-  // test('observing subtypes of WeakCollections(WeakMap, WeakSet)', () => {
-  //   // subtypes of WeakMap
-  //   class CustomMap extends WeakMap {}
-  //   const cmap = reactive(new CustomMap())
-
-  //   expect(cmap instanceof WeakMap).toBe(true)
-  //   expect(isReactive(cmap)).toBe(true)
-
-  //   const key = {}
-  //   cmap.set(key, {})
-  //   expect(isReactive(cmap.get(key))).toBe(true)
-
-  //   // subtypes of WeakSet
-  //   class CustomSet extends WeakSet {}
-  //   const cset = reactive(new CustomSet())
-
-  //   expect(cset instanceof WeakSet).toBe(true)
-  //   expect(isReactive(cset)).toBe(true)
-
-  //   let dummy
-  //   effect(() => (dummy = cset.has(key)))
-  //   expect(dummy).toBe(false)
-  //   cset.add(key)
-  //   expect(dummy).toBe(true)
-  //   cset.delete(key)
-  //   expect(dummy).toBe(false)
-  // })
-
-  test('observed value should proxy mutations to original (Object)', () => {
-    const original: any = { foo: 1 }
-    const observed = reactive(original)
-    // set
-    observed.bar = 1
-    expect(observed.bar).toBe(1)
-    expect(original.bar).toBe(1)
-    // delete
-    delete observed.foo
-    expect('foo' in observed).toBe(false)
-    expect('foo' in original).toBe(false)
-  })
-
-  test('original value change should reflect in observed value (Object)', () => {
-    const original: any = { foo: 1 }
-    const observed = reactive(original)
-    // set
-    original.bar = 1
-    expect(original.bar).toBe(1)
-    expect(observed.bar).toBe(1)
-    // delete
-    delete original.foo
-    expect('foo' in original).toBe(false)
-    expect('foo' in observed).toBe(false)
-  })
-
-  test('setting a property with an unobserved value should wrap with reactive', () => {
-    const observed = reactive<{ foo?: object }>({})
-    const raw = {}
-    set(observed, 'foo', raw)
-    // @discrepancy not a proxy
-    // expect(observed.foo).not.toBe(raw)
-    expect(isReactive(observed.foo)).toBe(true)
-  })
-
-  test('observing already observed value should return same Proxy', () => {
-    const original = { foo: 1 }
-    const observed = reactive(original)
-    const observed2 = reactive(observed)
-    expect(observed2).toBe(observed)
-  })
-
-  test('observing the same value multiple times should return same Proxy', () => {
-    const original = { foo: 1 }
-    const observed = reactive(original)
-    const observed2 = reactive(original)
-    expect(observed2).toBe(observed)
-  })
-
-  test('should not pollute original object with Proxies', () => {
-    const original: any = { foo: 1 }
-    const original2 = { bar: 2 }
-    const observed = reactive(original)
-    const observed2 = reactive(original2)
-    observed.bar = observed2
-    expect(observed.bar).toBe(observed2)
-    expect(original.bar).toBe(original2)
-  })
-
-  test('toRaw', () => {
-    const original = { foo: 1 }
-    const observed = reactive(original)
-    expect(toRaw(observed)).toBe(original)
-    expect(toRaw(original)).toBe(original)
-  })
-
-  test('toRaw on object using reactive as prototype', () => {
-    const original = reactive({})
-    const obj = Object.create(original)
-    const raw = toRaw(obj)
-    expect(raw).toBe(obj)
-    expect(raw).not.toBe(toRaw(original))
-  })
-
-  test('should not unwrap Ref<T>', () => {
-    const observedNumberRef = reactive(ref(1))
-    const observedObjectRef = reactive(ref({ foo: 1 }))
-
-    expect(isRef(observedNumberRef)).toBe(true)
-    expect(isRef(observedObjectRef)).toBe(true)
-  })
-
-  test('should unwrap computed refs', () => {
-    // readonly
-    const a = computed(() => 1)
-    // writable
-    const b = computed({
-      get: () => 1,
-      set: () => {}
-    })
-    const obj = reactive({ a, b })
-    // check type
-    obj.a + 1
-    obj.b + 1
-    expect(typeof obj.a).toBe(`number`)
-    expect(typeof obj.b).toBe(`number`)
-  })
-
-  test('should allow setting property from a ref to another ref', () => {
-    const foo = ref(0)
-    const bar = ref(1)
-    const observed = reactive({ a: foo })
-    let dummy
-    effect(() => {
-      dummy = observed.a
-    })
-    expect(dummy).toBe(0)
-
-    // @ts-ignore
-    observed.a = bar
-    expect(dummy).toBe(1)
-
-    bar.value++
-    expect(dummy).toBe(2)
-  })
-
-  test('non-observable values', () => {
-    const assertValue = (value: any) => {
-      reactive(value)
-      expect(
-        `value cannot be made reactive: ${String(value)}`
-      ).toHaveBeenWarnedLast()
-    }
-
-    // number
-    assertValue(1)
-    // string
-    assertValue('foo')
-    // boolean
-    assertValue(false)
-    // null
-    assertValue(null)
-    // undefined
-    assertValue(undefined)
-    // symbol
-    const s = Symbol()
-    assertValue(s)
-
-    // built-ins should work and return same value
-    const p = Promise.resolve()
-    expect(reactive(p)).toBe(p)
-    const r = new RegExp('')
-    expect(reactive(r)).toBe(r)
-    const d = new Date()
-    expect(reactive(d)).toBe(d)
-  })
-
-  test('markRaw', () => {
-    const obj = reactive({
-      foo: { a: 1 },
-      bar: markRaw({ b: 2 })
-    })
-    expect(isReactive(obj.foo)).toBe(true)
-    expect(isReactive(obj.bar)).toBe(false)
-  })
-
-  test('markRaw on non-extensible objects', () => {
-    const foo = Object.seal({})
-    markRaw(foo)
-    expect(isReactive(reactive(foo))).toBe(false)
-  })
-
-  test('should not observe non-extensible objects', () => {
-    const obj = reactive({
-      foo: Object.preventExtensions({ a: 1 }),
-      // sealed or frozen objects are considered non-extensible as well
-      bar: Object.freeze({ a: 1 }),
-      baz: Object.seal({ a: 1 })
-    })
-    expect(isReactive(obj.foo)).toBe(false)
-    expect(isReactive(obj.bar)).toBe(false)
-    expect(isReactive(obj.baz)).toBe(false)
-  })
-
-  test('should not observe objects with __v_skip', () => {
-    const original = {
-      foo: 1,
-      __v_skip: true
-    }
-    const observed = reactive(original)
-    expect(isReactive(observed)).toBe(false)
-  })
-
-  // #12595
-  test(`should not trigger if value didn't change`, () => {
-    const state = reactive({
-      foo: 1
-    })
-    const spy = vi.fn()
-    effect(() => {
-      state.foo
-      spy()
-    })
-    expect(spy).toHaveBeenCalledTimes(1)
-
-    state.foo = 1
-    expect(spy).toHaveBeenCalledTimes(1)
-
-    state.foo = NaN
-    expect(spy).toHaveBeenCalledTimes(2)
-
-    state.foo = NaN
-    expect(spy).toHaveBeenCalledTimes(2)
-
-    state.foo = 2
-    expect(spy).toHaveBeenCalledTimes(3)
-  })
-})
diff --git a/test/unit/features/v3/reactivity/readonly.spec.ts b/test/unit/features/v3/reactivity/readonly.spec.ts
deleted file mode 100644
index 252fb9884b7..00000000000
--- a/test/unit/features/v3/reactivity/readonly.spec.ts
+++ /dev/null
@@ -1,538 +0,0 @@
-import {
-  reactive,
-  readonly,
-  toRaw,
-  isReactive,
-  isReadonly,
-  markRaw,
-  ref,
-  isProxy
-} from 'v3'
-import { effect } from 'v3/reactivity/effect'
-import { set, del } from 'core/observer'
-
-/**
- * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html
- */
-type Writable<T> = { -readonly [P in keyof T]: T[P] }
-
-describe('reactivity/readonly', () => {
-  describe('Object', () => {
-    it('should make nested values readonly', () => {
-      const original = { foo: 1, bar: { baz: 2 } }
-      const wrapped = readonly(original)
-      expect(wrapped).not.toBe(original)
-      expect(isProxy(wrapped)).toBe(true)
-      expect(isReactive(wrapped)).toBe(false)
-      expect(isReadonly(wrapped)).toBe(true)
-      expect(isReactive(original)).toBe(false)
-      expect(isReadonly(original)).toBe(false)
-      expect(isReactive(wrapped.bar)).toBe(false)
-      expect(isReadonly(wrapped.bar)).toBe(true)
-      expect(isReactive(original.bar)).toBe(false)
-      expect(isReadonly(original.bar)).toBe(false)
-      // get
-      expect(wrapped.foo).toBe(1)
-      // has
-      expect('foo' in wrapped).toBe(true)
-      // ownKeys
-      expect(Object.keys(wrapped)).toEqual(['foo', 'bar'])
-    })
-
-    it('should not allow mutation', () => {
-      const qux = Symbol('qux')
-      const original = {
-        foo: 1,
-        bar: {
-          baz: 2
-        },
-        [qux]: 3
-      }
-      const wrapped: Writable<typeof original> = readonly(original)
-
-      wrapped.foo = 2
-      expect(wrapped.foo).toBe(1)
-      expect(
-        `Set operation on key "foo" failed: target is readonly.`
-      ).toHaveBeenWarnedLast()
-
-      set(wrapped.bar, `baz`, 3)
-      expect(wrapped.bar.baz).toBe(2)
-      expect(
-        `Set operation on key "baz" failed: target is readonly.`
-      ).toHaveBeenWarnedLast()
-
-      // @discrepancy: Vue 2 reactive system does not handle symbol keys.
-      // wrapped[qux] = 4
-      // expect(wrapped[qux]).toBe(3)
-      // expect(
-      //   `Set operation on key "Symbol(qux)" failed: target is readonly.`
-      // ).toHaveBeenWarnedLast()
-
-      del(wrapped, `foo`)
-      expect(wrapped.foo).toBe(1)
-      expect(
-        `Delete operation on key "foo" failed: target is readonly.`
-      ).toHaveBeenWarnedLast()
-
-      del(wrapped.bar, `baz`)
-      expect(wrapped.bar.baz).toBe(2)
-      expect(
-        `Delete operation on key "baz" failed: target is readonly.`
-      ).toHaveBeenWarnedLast()
-
-      // // @ts-expect-error
-      // delete wrapped[qux]
-      // expect(wrapped[qux]).toBe(3)
-      // expect(
-      //   `Delete operation on key "Symbol(qux)" failed: target is readonly.`
-      // ).toHaveBeenWarnedLast()
-    })
-
-    it('should not trigger effects', () => {
-      const wrapped: any = readonly({ a: 1 })
-      let dummy
-      effect(() => {
-        dummy = wrapped.a
-      })
-      expect(dummy).toBe(1)
-      wrapped.a = 2
-      expect(wrapped.a).toBe(1)
-      expect(dummy).toBe(1)
-      expect(`target is readonly`).toHaveBeenWarned()
-    })
-  })
-
-  // @discrepancy Vue 2 cannot support readonly array
-  // describe('Array', () => {
-  //   it('should make nested values readonly', () => {
-  //     const original = [{ foo: 1 }]
-  //     const wrapped = readonly(original)
-  //     expect(wrapped).not.toBe(original)
-  //     expect(isProxy(wrapped)).toBe(true)
-  //     expect(isReactive(wrapped)).toBe(false)
-  //     expect(isReadonly(wrapped)).toBe(true)
-  //     expect(isReactive(original)).toBe(false)
-  //     expect(isReadonly(original)).toBe(false)
-  //     expect(isReactive(wrapped[0])).toBe(false)
-  //     expect(isReadonly(wrapped[0])).toBe(true)
-  //     expect(isReactive(original[0])).toBe(false)
-  //     expect(isReadonly(original[0])).toBe(false)
-  //     // get
-  //     expect(wrapped[0].foo).toBe(1)
-  //     // has
-  //     expect(0 in wrapped).toBe(true)
-  //     // ownKeys
-  //     expect(Object.keys(wrapped)).toEqual(['0'])
-  //   })
-
-  //   it('should not allow mutation', () => {
-  //     const wrapped: any = readonly([{ foo: 1 }])
-  //     wrapped[0] = 1
-  //     expect(wrapped[0]).not.toBe(1)
-  //     expect(
-  //       `Set operation on key "0" failed: target is readonly.`
-  //     ).toHaveBeenWarned()
-  //     wrapped[0].foo = 2
-  //     expect(wrapped[0].foo).toBe(1)
-  //     expect(
-  //       `Set operation on key "foo" failed: target is readonly.`
-  //     ).toHaveBeenWarned()
-
-  //     // should block length mutation
-  //     wrapped.length = 0
-  //     expect(wrapped.length).toBe(1)
-  //     expect(wrapped[0].foo).toBe(1)
-  //     expect(
-  //       `Set operation on key "length" failed: target is readonly.`
-  //     ).toHaveBeenWarned()
-
-  //     // mutation methods invoke set/length internally and thus are blocked as well
-  //     wrapped.push(2)
-  //     expect(wrapped.length).toBe(1)
-  //     // push triggers two warnings on [1] and .length
-  //     expect(`target is readonly.`).toHaveBeenWarnedTimes(5)
-  //   })
-
-  //   it('should not trigger effects', () => {
-  //     const wrapped: any = readonly([{ a: 1 }])
-  //     let dummy
-  //     effect(() => {
-  //       dummy = wrapped[0].a
-  //     })
-  //     expect(dummy).toBe(1)
-  //     wrapped[0].a = 2
-  //     expect(wrapped[0].a).toBe(1)
-  //     expect(dummy).toBe(1)
-  //     expect(`target is readonly`).toHaveBeenWarnedTimes(1)
-  //     wrapped[0] = { a: 2 }
-  //     expect(wrapped[0].a).toBe(1)
-  //     expect(dummy).toBe(1)
-  //     expect(`target is readonly`).toHaveBeenWarnedTimes(2)
-  //   })
-  // })
-
-  // @discrepancy: Vue 2 doesn't support readonly on collection types
-  // const maps = [Map, WeakMap]
-  // maps.forEach((Collection: any) => {
-  //   describe(Collection.name, () => {
-  //     test('should make nested values readonly', () => {
-  //       const key1 = {}
-  //       const key2 = {}
-  //       const original = new Collection([
-  //         [key1, {}],
-  //         [key2, {}]
-  //       ])
-  //       const wrapped = readonly(original)
-  //       expect(wrapped).not.toBe(original)
-  //       expect(isProxy(wrapped)).toBe(true)
-  //       expect(isReactive(wrapped)).toBe(false)
-  //       expect(isReadonly(wrapped)).toBe(true)
-  //       expect(isReactive(original)).toBe(false)
-  //       expect(isReadonly(original)).toBe(false)
-  //       expect(isReactive(wrapped.get(key1))).toBe(false)
-  //       expect(isReadonly(wrapped.get(key1))).toBe(true)
-  //       expect(isReactive(original.get(key1))).toBe(false)
-  //       expect(isReadonly(original.get(key1))).toBe(false)
-  //     })
-
-  //     test('should not allow mutation & not trigger effect', () => {
-  //       const map = readonly(new Collection())
-  //       const key = {}
-  //       let dummy
-  //       effect(() => {
-  //         dummy = map.get(key)
-  //       })
-  //       expect(dummy).toBeUndefined()
-  //       map.set(key, 1)
-  //       expect(dummy).toBeUndefined()
-  //       expect(map.has(key)).toBe(false)
-  //       expect(
-  //         `Set operation on key "${key}" failed: target is readonly.`
-  //       ).toHaveBeenWarned()
-  //     })
-
-  //     // #1772
-  //     test('readonly + reactive should make get() value also readonly + reactive', () => {
-  //       const map = reactive(new Collection())
-  //       const roMap = readonly(map)
-  //       const key = {}
-  //       map.set(key, {})
-
-  //       const item = map.get(key)
-  //       expect(isReactive(item)).toBe(true)
-  //       expect(isReadonly(item)).toBe(false)
-
-  //       const roItem = roMap.get(key)
-  //       expect(isReactive(roItem)).toBe(true)
-  //       expect(isReadonly(roItem)).toBe(true)
-  //     })
-
-  //     if (Collection === Map) {
-  //       test('should retrieve readonly values on iteration', () => {
-  //         const key1 = {}
-  //         const key2 = {}
-  //         const original = new Map([
-  //           [key1, {}],
-  //           [key2, {}]
-  //         ])
-  //         const wrapped: any = readonly(original)
-  //         expect(wrapped.size).toBe(2)
-  //         for (const [key, value] of wrapped) {
-  //           expect(isReadonly(key)).toBe(true)
-  //           expect(isReadonly(value)).toBe(true)
-  //         }
-  //         wrapped.forEach((value: any) => {
-  //           expect(isReadonly(value)).toBe(true)
-  //         })
-  //         for (const value of wrapped.values()) {
-  //           expect(isReadonly(value)).toBe(true)
-  //         }
-  //       })
-
-  //       test('should retrieve reactive + readonly values on iteration', () => {
-  //         const key1 = {}
-  //         const key2 = {}
-  //         const original = reactive(
-  //           new Map([
-  //             [key1, {}],
-  //             [key2, {}]
-  //           ])
-  //         )
-  //         const wrapped: any = readonly(original)
-  //         expect(wrapped.size).toBe(2)
-  //         for (const [key, value] of wrapped) {
-  //           expect(isReadonly(key)).toBe(true)
-  //           expect(isReadonly(value)).toBe(true)
-  //           expect(isReactive(key)).toBe(true)
-  //           expect(isReactive(value)).toBe(true)
-  //         }
-  //         wrapped.forEach((value: any) => {
-  //           expect(isReadonly(value)).toBe(true)
-  //           expect(isReactive(value)).toBe(true)
-  //         })
-  //         for (const value of wrapped.values()) {
-  //           expect(isReadonly(value)).toBe(true)
-  //           expect(isReactive(value)).toBe(true)
-  //         }
-  //       })
-  //     }
-  //   })
-  // })
-
-  // const sets = [Set, WeakSet]
-  // sets.forEach((Collection: any) => {
-  //   describe(Collection.name, () => {
-  //     test('should make nested values readonly', () => {
-  //       const key1 = {}
-  //       const key2 = {}
-  //       const original = new Collection([key1, key2])
-  //       const wrapped = readonly(original)
-  //       expect(wrapped).not.toBe(original)
-  //       expect(isProxy(wrapped)).toBe(true)
-  //       expect(isReactive(wrapped)).toBe(false)
-  //       expect(isReadonly(wrapped)).toBe(true)
-  //       expect(isReactive(original)).toBe(false)
-  //       expect(isReadonly(original)).toBe(false)
-  //       expect(wrapped.has(reactive(key1))).toBe(true)
-  //       expect(original.has(reactive(key1))).toBe(false)
-  //     })
-
-  //     test('should not allow mutation & not trigger effect', () => {
-  //       const set = readonly(new Collection())
-  //       const key = {}
-  //       let dummy
-  //       effect(() => {
-  //         dummy = set.has(key)
-  //       })
-  //       expect(dummy).toBe(false)
-  //       set.add(key)
-  //       expect(dummy).toBe(false)
-  //       expect(set.has(key)).toBe(false)
-  //       expect(
-  //         `Add operation on key "${key}" failed: target is readonly.`
-  //       ).toHaveBeenWarned()
-  //     })
-
-  //     if (Collection === Set) {
-  //       test('should retrieve readonly values on iteration', () => {
-  //         const original = new Collection([{}, {}])
-  //         const wrapped: any = readonly(original)
-  //         expect(wrapped.size).toBe(2)
-  //         for (const value of wrapped) {
-  //           expect(isReadonly(value)).toBe(true)
-  //         }
-  //         wrapped.forEach((value: any) => {
-  //           expect(isReadonly(value)).toBe(true)
-  //         })
-  //         for (const value of wrapped.values()) {
-  //           expect(isReadonly(value)).toBe(true)
-  //         }
-  //         for (const [v1, v2] of wrapped.entries()) {
-  //           expect(isReadonly(v1)).toBe(true)
-  //           expect(isReadonly(v2)).toBe(true)
-  //         }
-  //       })
-  //     }
-  //   })
-  // })
-
-  test('calling reactive on an readonly should return readonly', () => {
-    const a = readonly({})
-    const b = reactive(a)
-    expect(isReadonly(b)).toBe(true)
-    // should point to same original
-    expect(toRaw(a)).toBe(toRaw(b))
-  })
-
-  test('calling readonly on a reactive object should return readonly', () => {
-    const a = reactive({})
-    const b = readonly(a)
-    expect(isReadonly(b)).toBe(true)
-    // should point to same original
-    expect(toRaw(a)).toBe(toRaw(b))
-  })
-
-  test('readonly should track and trigger if wrapping reactive original', () => {
-    const a = reactive({ n: 1 })
-    const b = readonly(a)
-    // should return true since it's wrapping a reactive source
-    expect(isReactive(b)).toBe(true)
-
-    let dummy
-    effect(() => {
-      dummy = b.n
-    })
-    expect(dummy).toBe(1)
-    a.n++
-    expect(b.n).toBe(2)
-    expect(dummy).toBe(2)
-  })
-
-  // test('readonly collection should not track', () => {
-  //   const map = new Map()
-  //   map.set('foo', 1)
-
-  //   const reMap = reactive(map)
-  //   const roMap = readonly(map)
-
-  //   let dummy
-  //   effect(() => {
-  //     dummy = roMap.get('foo')
-  //   })
-  //   expect(dummy).toBe(1)
-  //   reMap.set('foo', 2)
-  //   expect(roMap.get('foo')).toBe(2)
-  //   // should not trigger
-  //   expect(dummy).toBe(1)
-  // })
-
-  // test('readonly array should not track', () => {
-  //   const arr = [1]
-  //   const roArr = readonly(arr)
-
-  //   const eff = effect(() => {
-  //     roArr.includes(2)
-  //   })
-  //   expect(eff._watcher.deps.length).toBe(0)
-  // })
-
-  // test('readonly should track and trigger if wrapping reactive original (collection)', () => {
-  //   const a = reactive(new Map())
-  //   const b = readonly(a)
-  //   // should return true since it's wrapping a reactive source
-  //   expect(isReactive(b)).toBe(true)
-
-  //   a.set('foo', 1)
-
-  //   let dummy
-  //   effect(() => {
-  //     dummy = b.get('foo')
-  //   })
-  //   expect(dummy).toBe(1)
-  //   a.set('foo', 2)
-  //   expect(b.get('foo')).toBe(2)
-  //   expect(dummy).toBe(2)
-  // })
-
-  test('wrapping already wrapped value should return same Proxy', () => {
-    const original = { foo: 1 }
-    const wrapped = readonly(original)
-    const wrapped2 = readonly(wrapped)
-    expect(wrapped2).toBe(wrapped)
-  })
-
-  test('wrapping the same value multiple times should return same Proxy', () => {
-    const original = { foo: 1 }
-    const wrapped = readonly(original)
-    const wrapped2 = readonly(original)
-    expect(wrapped2).toBe(wrapped)
-  })
-
-  test('markRaw', () => {
-    const obj = readonly({
-      foo: { a: 1 },
-      bar: markRaw({ b: 2 })
-    })
-    expect(isReadonly(obj.foo)).toBe(true)
-    expect(isReactive(obj.bar)).toBe(false)
-  })
-
-  test('should make ref readonly', () => {
-    const n = readonly(ref(1))
-    // @ts-expect-error
-    n.value = 2
-    expect(n.value).toBe(1)
-    expect(
-      `Set operation on key "value" failed: target is readonly.`
-    ).toHaveBeenWarned()
-  })
-
-  // Test case not applicable to Vue 2
-  // https://github.com/vuejs/core/issues/3376
-  // test('calling readonly on computed should allow computed to set its private properties', () => {
-  //   const r = ref<boolean>(false)
-  //   const c = computed(() => r.value)
-  //   const rC = readonly(c)
-
-  //   r.value = true
-
-  //   expect(rC.value).toBe(true)
-  //   expect(
-  //     'Set operation on key "_dirty" failed: target is readonly.'
-  //   ).not.toHaveBeenWarned()
-  //   // @ts-expect-error - non-existent property
-  //   rC.randomProperty = true
-
-  //   expect(
-  //     'Set operation on key "randomProperty" failed: target is readonly.'
-  //   ).toHaveBeenWarned()
-  // })
-
-  // #4986
-  test('setting a readonly object as a property of a reactive object should retain readonly proxy', () => {
-    const r = readonly({})
-    const rr = reactive({}) as any
-    rr.foo = r
-    expect(rr.foo).toBe(r)
-    expect(isReadonly(rr.foo)).toBe(true)
-  })
-
-  test('attempting to write plain value to a readonly ref nested in a reactive object should fail', () => {
-    const r = ref(false)
-    const ror = readonly(r)
-    const obj = reactive({ ror })
-    obj.ror = true
-    expect(obj.ror).toBe(false)
-    expect(`Set operation on key "value" failed`).toHaveBeenWarned()
-  })
-
-  test('replacing a readonly ref nested in a reactive object with a new ref', () => {
-    const r = ref(false)
-    const ror = readonly(r)
-    const obj = reactive({ ror })
-    obj.ror = ref(true) as unknown as boolean
-    expect(obj.ror).toBe(true)
-    expect(toRaw(obj).ror).not.toBe(ror) // ref successfully replaced
-  })
-
-  test('setting readonly object to writable nested ref', () => {
-    const r = ref<any>()
-    const obj = reactive({ r })
-    const ro = readonly({})
-    obj.r = ro
-    expect(obj.r).toBe(ro)
-    expect(r.value).toBe(ro)
-  })
-
-  test('compatiblity with classes', () => {
-    const spy = vi.fn()
-    class Foo {
-      x = 1
-      log() {
-        spy(this.x)
-      }
-      change() {
-        this.x++
-      }
-    }
-    const foo = new Foo()
-    const readonlyFoo = readonly(foo)
-    readonlyFoo.log()
-    expect(spy).toHaveBeenCalledWith(1)
-
-    readonlyFoo.change()
-    expect(readonlyFoo.x).toBe(1)
-    expect(`et operation on key "x" failed`).toHaveBeenWarned()
-  })
-
-  test('warn non-extensible objects', () => {
-    const foo = Object.freeze({ a: 1 })
-    try {
-      readonly(foo)
-    } catch (e) {}
-    expect(
-      `Vue 2 does not support creating readonly proxy for non-extensible object`
-    ).toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/features/v3/reactivity/ref.spec.ts b/test/unit/features/v3/reactivity/ref.spec.ts
deleted file mode 100644
index 96212975359..00000000000
--- a/test/unit/features/v3/reactivity/ref.spec.ts
+++ /dev/null
@@ -1,421 +0,0 @@
-import {
-  ref,
-  isRef,
-  shallowRef,
-  unref,
-  triggerRef,
-  toRef,
-  toRefs,
-  customRef,
-  Ref,
-  isReactive,
-  isShallow,
-  reactive,
-  computed,
-  readonly
-} from 'v3'
-import { effect } from 'v3/reactivity/effect'
-
-describe('reactivity/ref', () => {
-  it('should hold a value', () => {
-    const a = ref(1)
-    expect(a.value).toBe(1)
-    a.value = 2
-    expect(a.value).toBe(2)
-  })
-
-  it('should be reactive', () => {
-    const a = ref(1)
-    let dummy
-    let calls = 0
-    effect(() => {
-      calls++
-      dummy = a.value
-    })
-    expect(calls).toBe(1)
-    expect(dummy).toBe(1)
-    a.value = 2
-    expect(calls).toBe(2)
-    expect(dummy).toBe(2)
-    // same value should not trigger
-    a.value = 2
-    expect(calls).toBe(2)
-  })
-
-  it('should make nested properties reactive', () => {
-    const a = ref({
-      count: 1
-    })
-    let dummy
-    effect(() => {
-      dummy = a.value.count
-    })
-    expect(dummy).toBe(1)
-    a.value.count = 2
-    expect(dummy).toBe(2)
-  })
-
-  it('should work without initial value', () => {
-    const a = ref()
-    let dummy
-    effect(() => {
-      dummy = a.value
-    })
-    expect(dummy).toBe(undefined)
-    a.value = 2
-    expect(dummy).toBe(2)
-  })
-
-  it('should work like a normal property when nested in a reactive object', () => {
-    const a = ref(1)
-    const obj = reactive({
-      a,
-      b: {
-        c: a
-      }
-    })
-
-    let dummy1: number
-    let dummy2: number
-
-    effect(() => {
-      dummy1 = obj.a
-      dummy2 = obj.b.c
-    })
-
-    const assertDummiesEqualTo = (val: number) =>
-      [dummy1, dummy2].forEach(dummy => expect(dummy).toBe(val))
-
-    assertDummiesEqualTo(1)
-    a.value++
-    assertDummiesEqualTo(2)
-    obj.a++
-    assertDummiesEqualTo(3)
-    obj.b.c++
-    assertDummiesEqualTo(4)
-  })
-
-  it('should unwrap nested ref in types', () => {
-    const a = ref(0)
-    const b = ref(a)
-
-    expect(typeof (b.value + 1)).toBe('number')
-  })
-
-  it('should unwrap nested values in types', () => {
-    const a = {
-      b: ref(0)
-    }
-
-    const c = ref(a)
-
-    expect(typeof (c.value.b + 1)).toBe('number')
-  })
-
-  it('should NOT unwrap ref types nested inside arrays', () => {
-    const arr = ref([1, ref(3)]).value
-    expect(isRef(arr[0])).toBe(false)
-    expect(isRef(arr[1])).toBe(true)
-    expect((arr[1] as Ref).value).toBe(3)
-  })
-
-  // @discrepancy Vue 2 does not observe array properties
-  // it('should unwrap ref types as props of arrays', () => {
-  //   const arr = [ref(0)]
-  //   const symbolKey = Symbol('')
-  //   arr['' as any] = ref(1)
-  //   arr[symbolKey as any] = ref(2)
-  //   const arrRef = ref(arr).value
-  //   expect(isRef(arrRef[0])).toBe(true)
-  //   expect(isRef(arrRef['' as any])).toBe(false)
-  //   expect(isRef(arrRef[symbolKey as any])).toBe(false)
-  //   expect(arrRef['' as any]).toBe(1)
-  //   expect(arrRef[symbolKey as any]).toBe(2)
-  // })
-
-  it('should keep tuple types', () => {
-    const tuple: [number, string, { a: number }, () => number, Ref<number>] = [
-      0,
-      '1',
-      { a: 1 },
-      () => 0,
-      ref(0)
-    ]
-    const tupleRef = ref(tuple)
-
-    tupleRef.value[0]++
-    expect(tupleRef.value[0]).toBe(1)
-    tupleRef.value[1] += '1'
-    expect(tupleRef.value[1]).toBe('11')
-    tupleRef.value[2].a++
-    expect(tupleRef.value[2].a).toBe(2)
-    expect(tupleRef.value[3]()).toBe(0)
-    tupleRef.value[4].value++
-    expect(tupleRef.value[4].value).toBe(1)
-  })
-
-  it('should keep symbols', () => {
-    const customSymbol = Symbol()
-    const obj = {
-      [Symbol.asyncIterator]: ref(1),
-      [Symbol.hasInstance]: { a: ref('a') },
-      [Symbol.isConcatSpreadable]: { b: ref(true) },
-      [Symbol.iterator]: [ref(1)],
-      [Symbol.match]: new Set<Ref<number>>(),
-      [Symbol.matchAll]: new Map<number, Ref<string>>(),
-      [Symbol.replace]: { arr: [ref('a')] },
-      [Symbol.search]: { set: new Set<Ref<number>>() },
-      [Symbol.species]: { map: new Map<number, Ref<string>>() },
-      [Symbol.split]: new WeakSet<Ref<boolean>>(),
-      [Symbol.toPrimitive]: new WeakMap<Ref<boolean>, string>(),
-      [Symbol.toStringTag]: { weakSet: new WeakSet<Ref<boolean>>() },
-      [Symbol.unscopables]: { weakMap: new WeakMap<Ref<boolean>, string>() },
-      [customSymbol]: { arr: [ref(1)] }
-    }
-
-    const objRef = ref(obj)
-
-    const keys: (keyof typeof obj)[] = [
-      Symbol.asyncIterator,
-      Symbol.hasInstance,
-      Symbol.isConcatSpreadable,
-      Symbol.iterator,
-      Symbol.match,
-      Symbol.matchAll,
-      Symbol.replace,
-      Symbol.search,
-      Symbol.species,
-      Symbol.split,
-      Symbol.toPrimitive,
-      Symbol.toStringTag,
-      Symbol.unscopables,
-      customSymbol
-    ]
-
-    keys.forEach(key => {
-      expect(objRef.value[key]).toStrictEqual(obj[key])
-    })
-  })
-
-  test('unref', () => {
-    expect(unref(1)).toBe(1)
-    expect(unref(ref(1))).toBe(1)
-  })
-
-  test('shallowRef', () => {
-    const sref = shallowRef({ a: 1 })
-    expect(isReactive(sref.value)).toBe(false)
-
-    let dummy
-    effect(() => {
-      dummy = sref.value.a
-    })
-    expect(dummy).toBe(1)
-
-    sref.value = { a: 2 }
-    expect(isReactive(sref.value)).toBe(false)
-    expect(dummy).toBe(2)
-  })
-
-  test('shallowRef force trigger', () => {
-    const sref = shallowRef({ a: 1 })
-    let dummy
-    effect(() => {
-      dummy = sref.value.a
-    })
-    expect(dummy).toBe(1)
-
-    sref.value.a = 2
-    expect(dummy).toBe(1) // should not trigger yet
-
-    // force trigger
-    triggerRef(sref)
-    expect(dummy).toBe(2)
-  })
-
-  test('shallowRef isShallow', () => {
-    expect(isShallow(shallowRef({ a: 1 }))).toBe(true)
-  })
-
-  test('isRef', () => {
-    expect(isRef(ref(1))).toBe(true)
-    expect(isRef(computed(() => 1))).toBe(true)
-
-    expect(isRef(0)).toBe(false)
-    expect(isRef(1)).toBe(false)
-    // an object that looks like a ref isn't necessarily a ref
-    expect(isRef({ value: 0 })).toBe(false)
-  })
-
-  test('toRef', () => {
-    const a = reactive({
-      x: 1
-    })
-    const x = toRef(a, 'x')
-    expect(isRef(x)).toBe(true)
-    expect(x.value).toBe(1)
-
-    // source -> proxy
-    a.x = 2
-    expect(x.value).toBe(2)
-
-    // proxy -> source
-    x.value = 3
-    expect(a.x).toBe(3)
-
-    // reactivity
-    let dummyX
-    effect(() => {
-      dummyX = x.value
-    })
-    expect(dummyX).toBe(x.value)
-
-    // mutating source should trigger effect using the proxy refs
-    a.x = 4
-    expect(dummyX).toBe(4)
-
-    // should keep ref
-    const r = { x: ref(1) }
-    expect(toRef(r, 'x')).toBe(r.x)
-  })
-
-  test('toRef default value', () => {
-    const a: { x: number | undefined } = { x: undefined }
-    const x = toRef(a, 'x', 1)
-    expect(x.value).toBe(1)
-
-    a.x = 2
-    expect(x.value).toBe(2)
-
-    a.x = undefined
-    expect(x.value).toBe(1)
-  })
-
-  test('toRefs', () => {
-    const a = reactive({
-      x: 1,
-      y: 2
-    })
-
-    const { x, y } = toRefs(a)
-
-    expect(isRef(x)).toBe(true)
-    expect(isRef(y)).toBe(true)
-    expect(x.value).toBe(1)
-    expect(y.value).toBe(2)
-
-    // source -> proxy
-    a.x = 2
-    a.y = 3
-    expect(x.value).toBe(2)
-    expect(y.value).toBe(3)
-
-    // proxy -> source
-    x.value = 3
-    y.value = 4
-    expect(a.x).toBe(3)
-    expect(a.y).toBe(4)
-
-    // reactivity
-    let dummyX, dummyY
-    effect(() => {
-      dummyX = x.value
-      dummyY = y.value
-    })
-    expect(dummyX).toBe(x.value)
-    expect(dummyY).toBe(y.value)
-
-    // mutating source should trigger effect using the proxy refs
-    a.x = 4
-    a.y = 5
-    expect(dummyX).toBe(4)
-    expect(dummyY).toBe(5)
-  })
-
-  test('toRefs should warn on plain object', () => {
-    toRefs({})
-    expect(`toRefs() expects a reactive object`).toHaveBeenWarned()
-  })
-
-  test('toRefs should warn on plain array', () => {
-    toRefs([])
-    expect(`toRefs() expects a reactive object`).toHaveBeenWarned()
-  })
-
-  test('toRefs reactive array', () => {
-    const { arr } = reactive({ arr: ['a', 'b', 'c'] })
-    const refs = toRefs(arr)
-
-    expect(Array.isArray(refs)).toBe(true)
-
-    refs[0].value = '1'
-    expect(arr[0]).toBe('1')
-
-    arr[1] = '2'
-    expect(refs[1].value).toBe('2')
-  })
-
-  test('customRef', () => {
-    let value = 1
-    let _trigger: () => void
-
-    const custom = customRef((track, trigger) => ({
-      get() {
-        track()
-        return value
-      },
-      set(newValue: number) {
-        value = newValue
-        _trigger = trigger
-      }
-    }))
-
-    expect(isRef(custom)).toBe(true)
-
-    let dummy
-    effect(() => {
-      dummy = custom.value
-    })
-    expect(dummy).toBe(1)
-
-    custom.value = 2
-    // should not trigger yet
-    expect(dummy).toBe(1)
-
-    _trigger!()
-    expect(dummy).toBe(2)
-  })
-
-  test('should not trigger when setting value to same proxy', () => {
-    const obj = reactive({ count: 0 })
-
-    const a = ref(obj)
-    const spy1 = vi.fn(() => a.value)
-
-    effect(spy1)
-
-    a.value = obj
-    expect(spy1).toBeCalledTimes(1)
-
-    const b = shallowRef(obj)
-    const spy2 = vi.fn(() => b.value)
-
-    effect(spy2)
-
-    b.value = obj
-    expect(spy2).toBeCalledTimes(1)
-  })
-
-  test('ref should preserve value readonly-ness', () => {
-    const original = {}
-    const r = reactive(original)
-    const rr = readonly(original)
-    const a = ref(original)
-
-    expect(a.value).toBe(r)
-
-    a.value = rr
-    expect(a.value).toBe(rr)
-    expect(a.value).not.toBe(r)
-  })
-})
diff --git a/test/unit/features/v3/reactivity/shallowReactive.spec.ts b/test/unit/features/v3/reactivity/shallowReactive.spec.ts
deleted file mode 100644
index d1125771b90..00000000000
--- a/test/unit/features/v3/reactivity/shallowReactive.spec.ts
+++ /dev/null
@@ -1,193 +0,0 @@
-import {
-  isReactive,
-  isRef,
-  isShallow,
-  reactive,
-  Ref,
-  ref,
-  shallowReactive,
-  shallowReadonly
-} from 'v3'
-
-describe('shallowReactive', () => {
-  test('should not make non-reactive properties reactive', () => {
-    const props = shallowReactive({ n: { foo: 1 } })
-    expect(isReactive(props.n)).toBe(false)
-  })
-
-  test('should keep reactive properties reactive', () => {
-    const props: any = shallowReactive({ n: reactive({ foo: 1 }) })
-    props.n = reactive({ foo: 2 })
-    expect(isReactive(props.n)).toBe(true)
-  })
-
-  test('isShallow', () => {
-    expect(isShallow(shallowReactive({}))).toBe(true)
-    expect(isShallow(shallowReadonly({}))).toBe(true)
-  })
-
-  // #5271
-  test('should respect shallow reactive nested inside reactive on reset', () => {
-    const r = reactive({ foo: shallowReactive({ bar: {} }) })
-    expect(isShallow(r.foo)).toBe(true)
-    expect(isReactive(r.foo.bar)).toBe(false)
-
-    r.foo = shallowReactive({ bar: {} })
-    expect(isShallow(r.foo)).toBe(true)
-    expect(isReactive(r.foo.bar)).toBe(false)
-  })
-
-  // #12597
-  test('should not unwrap refs', () => {
-    const foo = shallowReactive({
-      bar: ref(123)
-    })
-    expect(isRef(foo.bar)).toBe(true)
-    expect(foo.bar.value).toBe(123)
-  })
-
-  // #12688
-  test('should not mutate refs', () => {
-    const original = ref(123)
-    const foo = shallowReactive<{ bar: Ref<number> | number }>({
-      bar: original
-    })
-    expect(foo.bar).toBe(original)
-    foo.bar = 234
-    expect(foo.bar).toBe(234)
-    expect(original.value).toBe(123)
-  })
-
-  // @discrepancy no shallow/non-shallow versions from the same source -
-  // cannot support this without real proxies
-  // #2843
-  // test('should allow shallow and normal reactive for same target', () => {
-  //   const original = { foo: {} }
-  //   const shallowProxy = shallowReactive(original)
-  //   const reactiveProxy = reactive(original)
-  //   expect(shallowProxy).not.toBe(reactiveProxy)
-  //   expect(isReactive(shallowProxy.foo)).toBe(false)
-  //   expect(isReactive(reactiveProxy.foo)).toBe(true)
-  // })
-
-  // test('should respect shallow/deep versions of same target on access', () => {
-  //   const original = {}
-  //   const shallow = shallowReactive(original)
-  //   const deep = reactive(original)
-  //   const r = reactive({ shallow, deep })
-  //   expect(r.shallow).toBe(shallow)
-  //   expect(r.deep).toBe(deep)
-  // })
-
-  // @discrepancy Vue 2 does not support collections
-  // describe('collections', () => {
-  //   test('should be reactive', () => {
-  //     const shallowSet = shallowReactive(new Set())
-  //     const a = {}
-  //     let size
-
-  //     effect(() => {
-  //       size = shallowSet.size
-  //     })
-
-  //     expect(size).toBe(0)
-
-  //     shallowSet.add(a)
-  //     expect(size).toBe(1)
-
-  //     shallowSet.delete(a)
-  //     expect(size).toBe(0)
-  //   })
-
-  //   test('should not observe when iterating', () => {
-  //     const shallowSet = shallowReactive(new Set())
-  //     const a = {}
-  //     shallowSet.add(a)
-
-  //     const spreadA = [...shallowSet][0]
-  //     expect(isReactive(spreadA)).toBe(false)
-  //   })
-
-  //   test('should not get reactive entry', () => {
-  //     const shallowMap = shallowReactive(new Map())
-  //     const a = {}
-  //     const key = 'a'
-
-  //     shallowMap.set(key, a)
-
-  //     expect(isReactive(shallowMap.get(key))).toBe(false)
-  //   })
-
-  //   test('should not get reactive on foreach', () => {
-  //     const shallowSet = shallowReactive(new Set())
-  //     const a = {}
-  //     shallowSet.add(a)
-
-  //     shallowSet.forEach(x => expect(isReactive(x)).toBe(false))
-  //   })
-
-  //   // #1210
-  //   test('onTrack on called on objectSpread', () => {
-  //     const onTrackFn = vi.fn()
-  //     const shallowSet = shallowReactive(new Set())
-  //     let a
-  //     effect(
-  //       () => {
-  //         a = Array.from(shallowSet)
-  //       },
-  //       {
-  //         onTrack: onTrackFn
-  //       }
-  //     )
-
-  //     expect(a).toMatchObject([])
-  //     expect(onTrackFn).toHaveBeenCalled()
-  //   })
-  // })
-
-  // @discrepancy Vue 2 does not track array without access
-  // describe('array', () => {
-  //   test('should be reactive', () => {
-  //     const shallowArray = shallowReactive<unknown[]>([])
-  //     const a = {}
-  //     let size
-
-  //     effect(() => {
-  //       size = shallowArray.length
-  //     })
-
-  //     expect(size).toBe(0)
-
-  //     shallowArray.push(a)
-  //     expect(size).toBe(1)
-
-  //     shallowArray.pop()
-  //     expect(size).toBe(0)
-  //   })
-  //   test('should not observe when iterating', () => {
-  //     const shallowArray = shallowReactive<object[]>([])
-  //     const a = {}
-  //     shallowArray.push(a)
-
-  //     const spreadA = [...shallowArray][0]
-  //     expect(isReactive(spreadA)).toBe(false)
-  //   })
-
-  //   test('onTrack on called on objectSpread', () => {
-  //     const onTrackFn = vi.fn()
-  //     const shallowArray = shallowReactive([])
-  //     let a
-  //     effect(
-  //       () => {
-  //         a = Array.from(shallowArray)
-  //       },
-  //       {
-  //         onTrack: onTrackFn
-  //       }
-  //     )
-
-  //     expect(a).toMatchObject([])
-  //     expect(onTrackFn).toHaveBeenCalled()
-  //   })
-  // })
-})
diff --git a/test/unit/features/v3/reactivity/shallowReadonly.spec.ts b/test/unit/features/v3/reactivity/shallowReadonly.spec.ts
deleted file mode 100644
index 6ac3578c675..00000000000
--- a/test/unit/features/v3/reactivity/shallowReadonly.spec.ts
+++ /dev/null
@@ -1,206 +0,0 @@
-import { isReactive, shallowReadonly, readonly, isReadonly } from 'v3'
-
-describe('reactivity/shallowReadonly', () => {
-  test('should be readonly', () => {
-    expect(isReadonly(shallowReadonly({}))).toBe(true)
-  })
-
-  test('should not make non-reactive properties reactive', () => {
-    const props = shallowReadonly({ n: { foo: 1 } })
-    expect(isReactive(props.n)).toBe(false)
-  })
-
-  test('should make root level properties readonly', () => {
-    const props = shallowReadonly({ n: 1 })
-    // @ts-expect-error
-    props.n = 2
-    expect(props.n).toBe(1)
-    expect(
-      `Set operation on key "n" failed: target is readonly.`
-    ).toHaveBeenWarned()
-  })
-
-  // to retain 2.x behavior.
-  test('should NOT make nested properties readonly', () => {
-    const props = shallowReadonly({ n: { foo: 1 } })
-
-    props.n.foo = 2
-    expect(props.n.foo).toBe(2)
-    expect(
-      `Set operation on key "foo" failed: target is readonly.`
-    ).not.toHaveBeenWarned()
-  })
-
-  // #2843
-  test('should differentiate from normal readonly calls', () => {
-    const original = { foo: {} }
-    const shallowProxy = shallowReadonly(original)
-    const reactiveProxy = readonly(original)
-    expect(shallowProxy).not.toBe(reactiveProxy)
-    expect(isReadonly(shallowProxy.foo)).toBe(false)
-    expect(isReadonly(reactiveProxy.foo)).toBe(true)
-  })
-
-  // @discrepancy does not support collections
-  // describe('collection/Map', () => {
-  //   ;[Map, WeakMap].forEach(Collection => {
-  //     test('should make the map/weak-map readonly', () => {
-  //       const key = {}
-  //       const val = { foo: 1 }
-  //       const original = new Collection([[key, val]])
-  //       const sroMap = shallowReadonly(original)
-  //       expect(isReadonly(sroMap)).toBe(true)
-  //       expect(isReactive(sroMap)).toBe(false)
-  //       expect(sroMap.get(key)).toBe(val)
-
-  //       sroMap.set(key, {} as any)
-  //       expect(
-  //         `Set operation on key "[object Object]" failed: target is readonly.`
-  //       ).toHaveBeenWarned()
-  //     })
-
-  //     test('should not make nested values readonly', () => {
-  //       const key = {}
-  //       const val = { foo: 1 }
-  //       const original = new Collection([[key, val]])
-  //       const sroMap = shallowReadonly(original)
-  //       expect(isReadonly(sroMap.get(key))).toBe(false)
-  //       expect(isReactive(sroMap.get(key))).toBe(false)
-
-  //       sroMap.get(key)!.foo = 2
-  //       expect(
-  //         `Set operation on key "foo" failed: target is readonly.`
-  //       ).not.toHaveBeenWarned()
-  //     })
-  //   })
-
-  //   test('should not make the value generated by the iterable method readonly', () => {
-  //     const key = {}
-  //     const val = { foo: 1 }
-  //     const original = new Map([[key, val]])
-  //     const sroMap = shallowReadonly(original)
-
-  //     const values1 = [...sroMap.values()]
-  //     const values2 = [...sroMap.entries()]
-
-  //     expect(isReadonly(values1[0])).toBe(false)
-  //     expect(isReactive(values1[0])).toBe(false)
-  //     expect(values1[0]).toBe(val)
-
-  //     values1[0].foo = 2
-  //     expect(
-  //       `Set operation on key "foo" failed: target is readonly.`
-  //     ).not.toHaveBeenWarned()
-
-  //     expect(isReadonly(values2[0][1])).toBe(false)
-  //     expect(isReactive(values2[0][1])).toBe(false)
-  //     expect(values2[0][1]).toBe(val)
-
-  //     values2[0][1].foo = 2
-  //     expect(
-  //       `Set operation on key "foo" failed: target is readonly.`
-  //     ).not.toHaveBeenWarned()
-  //   })
-
-  //   test('should not make the value generated by the forEach method readonly', () => {
-  //     const val = { foo: 1 }
-  //     const original = new Map([['key', val]])
-  //     const sroMap = shallowReadonly(original)
-
-  //     sroMap.forEach(val => {
-  //       expect(isReadonly(val)).toBe(false)
-  //       expect(isReactive(val)).toBe(false)
-  //       expect(val).toBe(val)
-
-  //       val.foo = 2
-  //       expect(
-  //         `Set operation on key "foo" failed: target is readonly.`
-  //       ).not.toHaveBeenWarned()
-  //     })
-  //   })
-  // })
-
-  // describe('collection/Set', () => {
-  //   test('should make the set/weak-set readonly', () => {
-  //     ;[Set, WeakSet].forEach(Collection => {
-  //       const obj = { foo: 1 }
-  //       const original = new Collection([obj])
-  //       const sroSet = shallowReadonly(original)
-  //       expect(isReadonly(sroSet)).toBe(true)
-  //       expect(isReactive(sroSet)).toBe(false)
-  //       expect(sroSet.has(obj)).toBe(true)
-
-  //       sroSet.add({} as any)
-  //       expect(
-  //         `Add operation on key "[object Object]" failed: target is readonly.`
-  //       ).toHaveBeenWarned()
-  //     })
-  //   })
-
-  //   test('should not make nested values readonly', () => {
-  //     const obj = { foo: 1 }
-  //     const original = new Set([obj])
-  //     const sroSet = shallowReadonly(original)
-
-  //     const values = [...sroSet.values()]
-
-  //     expect(values[0]).toBe(obj)
-  //     expect(isReadonly(values[0])).toBe(false)
-  //     expect(isReactive(values[0])).toBe(false)
-
-  //     sroSet.add({} as any)
-  //     expect(
-  //       `Add operation on key "[object Object]" failed: target is readonly.`
-  //     ).toHaveBeenWarned()
-
-  //     values[0].foo = 2
-  //     expect(
-  //       `Set operation on key "foo" failed: target is readonly.`
-  //     ).not.toHaveBeenWarned()
-  //   })
-
-  //   test('should not make the value generated by the iterable method readonly', () => {
-  //     const val = { foo: 1 }
-  //     const original = new Set([val])
-  //     const sroSet = shallowReadonly(original)
-
-  //     const values1 = [...sroSet.values()]
-  //     const values2 = [...sroSet.entries()]
-
-  //     expect(isReadonly(values1[0])).toBe(false)
-  //     expect(isReactive(values1[0])).toBe(false)
-  //     expect(values1[0]).toBe(val)
-
-  //     values1[0].foo = 2
-  //     expect(
-  //       `Set operation on key "foo" failed: target is readonly.`
-  //     ).not.toHaveBeenWarned()
-
-  //     expect(isReadonly(values2[0][1])).toBe(false)
-  //     expect(isReactive(values2[0][1])).toBe(false)
-  //     expect(values2[0][1]).toBe(val)
-
-  //     values2[0][1].foo = 2
-  //     expect(
-  //       `Set operation on key "foo" failed: target is readonly.`
-  //     ).not.toHaveBeenWarned()
-  //   })
-
-  //   test('should not make the value generated by the forEach method readonly', () => {
-  //     const val = { foo: 1 }
-  //     const original = new Set([val])
-  //     const sroSet = shallowReadonly(original)
-
-  //     sroSet.forEach(val => {
-  //       expect(isReadonly(val)).toBe(false)
-  //       expect(isReactive(val)).toBe(false)
-  //       expect(val).toBe(val)
-
-  //       val.foo = 2
-  //       expect(
-  //         `Set operation on key "foo" failed: target is readonly.`
-  //       ).not.toHaveBeenWarned()
-  //     })
-  //   })
-  // })
-})
diff --git a/test/unit/features/v3/setupTemplateRef.spec.ts b/test/unit/features/v3/setupTemplateRef.spec.ts
deleted file mode 100644
index 5ac6b879875..00000000000
--- a/test/unit/features/v3/setupTemplateRef.spec.ts
+++ /dev/null
@@ -1,501 +0,0 @@
-import Vue from 'vue'
-import { ref, h, nextTick, reactive } from 'v3/index'
-
-// reference: https://vue-composition-api-rfc.netlify.com/api.html#template-refs
-
-describe('api: setup() template refs', () => {
-  it('string ref mount', () => {
-    const el = ref(null)
-
-    const Comp = {
-      setup() {
-        return {
-          refKey: el
-        }
-      },
-      render() {
-        return h('div', { ref: 'refKey' })
-      }
-    }
-    const vm = new Vue(Comp).$mount()
-    expect(el.value).toBe(vm.$el)
-  })
-
-  it('string ref update', async () => {
-    const fooEl = ref(null)
-    const barEl = ref(null)
-    const refKey = ref('foo')
-
-    const Comp = {
-      setup() {
-        return {
-          foo: fooEl,
-          bar: barEl
-        }
-      },
-      render() {
-        return h('div', { ref: refKey.value })
-      }
-    }
-    const vm = new Vue(Comp).$mount()
-    expect(barEl.value).toBe(null)
-
-    refKey.value = 'bar'
-    await nextTick()
-    expect(fooEl.value).toBe(null)
-    expect(barEl.value).toBe(vm.$el)
-  })
-
-  it('string ref unmount', async () => {
-    const el = ref(null)
-    const toggle = ref(true)
-
-    const Comp = {
-      setup() {
-        return {
-          refKey: el
-        }
-      },
-      render() {
-        return toggle.value ? h('div', { ref: 'refKey' }) : null
-      }
-    }
-
-    const vm = new Vue(Comp).$mount()
-    expect(el.value).toBe(vm.$el)
-
-    toggle.value = false
-    await nextTick()
-    expect(el.value).toBe(null)
-  })
-
-  it('function ref mount', () => {
-    const fn = vi.fn()
-
-    const Comp = {
-      render: () => h('div', { ref: fn })
-    }
-    const vm = new Vue(Comp).$mount()
-    expect(fn.mock.calls[0][0]).toBe(vm.$el)
-  })
-
-  it('function ref update', async () => {
-    const fn1 = vi.fn()
-    const fn2 = vi.fn()
-    const fn = ref(fn1)
-
-    const Comp = { render: () => h('div', { ref: fn.value }) }
-
-    const vm = new Vue(Comp).$mount()
-    expect(fn1.mock.calls).toHaveLength(1)
-    expect(fn1.mock.calls[0][0]).toBe(vm.$el)
-    expect(fn2.mock.calls).toHaveLength(0)
-
-    fn.value = fn2
-    await nextTick()
-    expect(fn1.mock.calls).toHaveLength(2)
-    expect(fn1.mock.calls[1][0]).toBe(null)
-    expect(fn2.mock.calls).toHaveLength(1)
-    expect(fn2.mock.calls[0][0]).toBe(vm.$el)
-  })
-
-  it('function ref unmount', async () => {
-    const fn = vi.fn()
-    const toggle = ref(true)
-
-    const Comp = {
-      render: () => (toggle.value ? h('div', { ref: fn }) : null)
-    }
-    const vm = new Vue(Comp).$mount()
-    expect(fn.mock.calls[0][0]).toBe(vm.$el)
-    toggle.value = false
-    await nextTick()
-    expect(fn.mock.calls[1][0]).toBe(null)
-  })
-
-  it('render function ref mount', () => {
-    const el = ref(null)
-
-    const Comp = {
-      setup() {
-        return () => h('div', { ref: el })
-      }
-    }
-    const vm = new Vue(Comp).$mount()
-    expect(el.value).toBe(vm.$el)
-  })
-
-  it('render function ref update', async () => {
-    const refs = {
-      foo: ref(null),
-      bar: ref(null)
-    }
-    const refKey = ref<keyof typeof refs>('foo')
-
-    const Comp = {
-      setup() {
-        return () => h('div', { ref: refs[refKey.value] })
-      }
-    }
-    const vm = new Vue(Comp).$mount()
-    expect(refs.foo.value).toBe(vm.$el)
-    expect(refs.bar.value).toBe(null)
-
-    refKey.value = 'bar'
-    await nextTick()
-    expect(refs.foo.value).toBe(null)
-    expect(refs.bar.value).toBe(vm.$el)
-  })
-
-  it('render function ref unmount', async () => {
-    const el = ref(null)
-    const toggle = ref(true)
-
-    const Comp = {
-      setup() {
-        return () => (toggle.value ? h('div', { ref: el }) : null)
-      }
-    }
-    const vm = new Vue(Comp).$mount()
-    expect(el.value).toBe(vm.$el)
-
-    toggle.value = false
-    await nextTick()
-    expect(el.value).toBe(null)
-  })
-
-  it('string ref inside slots', async () => {
-    const spy = vi.fn()
-    const Child = {
-      render(this: any) {
-        return this.$slots.default
-      }
-    }
-
-    const Comp = {
-      render() {
-        return h(Child, [h('div', { ref: 'foo' })])
-      },
-      mounted(this: any) {
-        spy(this.$refs.foo.tagName)
-      }
-    }
-    new Vue(Comp).$mount()
-    expect(spy).toHaveBeenCalledWith('DIV')
-  })
-
-  it('string ref inside scoped slots', async () => {
-    const spy = vi.fn()
-    const Child = {
-      render(this: any) {
-        return this.$scopedSlots.default()
-      }
-    }
-
-    const Comp = {
-      render() {
-        return h(Child, {
-          scopedSlots: {
-            default: () => [h('div', { ref: 'foo' })]
-          }
-        })
-      },
-      mounted(this: any) {
-        spy(this.$refs.foo.tagName)
-      }
-    }
-    new Vue(Comp).$mount()
-    expect(spy).toHaveBeenCalledWith('DIV')
-  })
-
-  it('should work with direct reactive property', () => {
-    const state = reactive({
-      refKey: null
-    })
-
-    const Comp = {
-      setup() {
-        return state
-      },
-      render() {
-        return h('div', { ref: 'refKey' })
-      }
-    }
-    const vm = new Vue(Comp).$mount()
-    expect(state.refKey).toBe(vm.$el)
-  })
-
-  test('multiple refs', () => {
-    const refKey1 = ref(null)
-    const refKey2 = ref(null)
-    const refKey3 = ref(null)
-
-    const Comp = {
-      setup() {
-        return {
-          refKey1,
-          refKey2,
-          refKey3
-        }
-      },
-      render() {
-        return h('div', [
-          h('div', { ref: 'refKey1' }),
-          h('div', { ref: 'refKey2' }),
-          h('div', { ref: 'refKey3' })
-        ])
-      }
-    }
-    const vm = new Vue(Comp).$mount()
-    expect(refKey1.value).toBe(vm.$el.children[0])
-    expect(refKey2.value).toBe(vm.$el.children[1])
-    expect(refKey3.value).toBe(vm.$el.children[2])
-  })
-
-  // vuejs/core#1505
-  test('reactive template ref in the same template', async () => {
-    const Comp = {
-      setup() {
-        const el = ref()
-        return { el }
-      },
-      render(this: any) {
-        return h(
-          'div',
-          { attrs: { id: 'foo' }, ref: 'el' },
-          this.el && this.el.id
-        )
-      }
-    }
-
-    const vm = new Vue(Comp).$mount()
-    // ref not ready on first render, but should queue an update immediately
-    expect(vm.$el.outerHTML).toBe(`<div id="foo"></div>`)
-    await nextTick()
-    // ref should be updated
-    expect(vm.$el.outerHTML).toBe(`<div id="foo">foo</div>`)
-  })
-
-  // vuejs/core#1834
-  test('exchange refs', async () => {
-    const refToggle = ref(false)
-    const spy = vi.fn()
-
-    const Comp = {
-      render(this: any) {
-        return h('div', [
-          h('p', { ref: refToggle.value ? 'foo' : 'bar' }),
-          h('i', { ref: refToggle.value ? 'bar' : 'foo' })
-        ])
-      },
-      mounted(this: any) {
-        spy(this.$refs.foo.tagName, this.$refs.bar.tagName)
-      },
-      updated(this: any) {
-        spy(this.$refs.foo.tagName, this.$refs.bar.tagName)
-      }
-    }
-
-    new Vue(Comp).$mount()
-
-    expect(spy.mock.calls[0][0]).toBe('I')
-    expect(spy.mock.calls[0][1]).toBe('P')
-    refToggle.value = true
-    await nextTick()
-    expect(spy.mock.calls[1][0]).toBe('P')
-    expect(spy.mock.calls[1][1]).toBe('I')
-  })
-
-  // vuejs/core#1789
-  test('toggle the same ref to different elements', async () => {
-    const refToggle = ref(false)
-    const spy = vi.fn()
-
-    const Comp = {
-      render(this: any) {
-        return refToggle.value ? h('p', { ref: 'foo' }) : h('i', { ref: 'foo' })
-      },
-      mounted(this: any) {
-        spy(this.$refs.foo.tagName)
-      },
-      updated(this: any) {
-        spy(this.$refs.foo.tagName)
-      }
-    }
-
-    new Vue(Comp).$mount()
-
-    expect(spy.mock.calls[0][0]).toBe('I')
-    refToggle.value = true
-    await nextTick()
-    expect(spy.mock.calls[1][0]).toBe('P')
-  })
-
-  // vuejs/core#2078
-  // @discrepancy Vue 2 doesn't handle merge refs
-  // test('handling multiple merged refs', async () => {
-  //   const Foo = {
-  //     render: () => h('div', 'foo')
-  //   }
-  //   const Bar = {
-  //     render: () => h('div', 'bar')
-  //   }
-
-  //   const viewRef = shallowRef<any>(Foo)
-  //   const elRef1 = ref()
-  //   const elRef2 = ref()
-
-  //   const App = {
-  //     render() {
-  //       if (!viewRef.value) {
-  //         return null
-  //       }
-  //       const view = h(viewRef.value, { ref: elRef1 })
-  //       return h(view, { ref: elRef2 })
-  //     }
-  //   }
-
-  //   new Vue(App).$mount()
-
-  //   expect(elRef1.value.$el.innerHTML).toBe('foo')
-  //   expect(elRef1.value).toBe(elRef2.value)
-
-  //   viewRef.value = Bar
-  //   await nextTick()
-  //   expect(elRef1.value.$el.innerHTML).toBe('bar')
-  //   expect(elRef1.value).toBe(elRef2.value)
-
-  //   viewRef.value = null
-  //   await nextTick()
-  //   expect(elRef1.value).toBeNull()
-  //   expect(elRef1.value).toBe(elRef2.value)
-  // })
-
-  // Vue 2 doesn't have inline mode
-  // test('raw ref with ref_key', () => {
-  //   let refs: any
-
-  //   const el = ref()
-
-  //   const App = {
-  //     mounted() {
-  //       refs = (this as any).$refs
-  //     },
-  //     render() {
-  //       return h(
-  //         'div',
-  //         {
-  //           ref: el,
-  //           ref_key: 'el'
-  //         },
-  //         'hello'
-  //       )
-  //     }
-  //   }
-
-  //   new Vue(App).$mount()
-
-  //   expect(el.value.innerHTML).toBe('hello')
-  //   expect(refs.el.innerHTML).toBe('hello')
-  // })
-
-  // compiled output of v-for + template ref
-  test('ref in v-for', async () => {
-    const show = ref(true)
-    const state = reactive({ list: [1, 2, 3] })
-    const listRefs = ref<any[]>([])
-    const mapRefs = () => listRefs.value.map(n => n.innerHTML)
-
-    const App = {
-      render() {
-        return show.value
-          ? h(
-              'ul',
-              state.list.map(i =>
-                h(
-                  'li',
-                  {
-                    ref: listRefs,
-                    refInFor: true
-                  },
-                  i
-                )
-              )
-            )
-          : null
-      }
-    }
-
-    new Vue(App).$mount()
-
-    expect(mapRefs()).toMatchObject(['1', '2', '3'])
-
-    state.list.push(4)
-    await nextTick()
-    expect(mapRefs()).toMatchObject(['1', '2', '3', '4'])
-
-    state.list.shift()
-    await nextTick()
-    expect(mapRefs()).toMatchObject(['2', '3', '4'])
-
-    show.value = !show.value
-    await nextTick()
-
-    expect(mapRefs()).toMatchObject([])
-
-    show.value = !show.value
-    await nextTick()
-    expect(mapRefs()).toMatchObject(['2', '3', '4'])
-  })
-
-  test('named ref in v-for', async () => {
-    const show = ref(true)
-    const state = reactive({ list: [1, 2, 3] })
-    const listRefs = ref([])
-    const mapRefs = () => listRefs.value.map((n: HTMLElement) => n.innerHTML)
-
-    const App = {
-      setup() {
-        return { listRefs }
-      },
-      render() {
-        return show.value
-          ? h(
-              'ul',
-              state.list.map(i =>
-                h(
-                  'li',
-                  {
-                    ref: 'listRefs',
-                    refInFor: true
-                  },
-                  i
-                )
-              )
-            )
-          : null
-      }
-    }
-
-    new Vue(App).$mount()
-
-    expect(mapRefs()).toMatchObject(['1', '2', '3'])
-
-    state.list.push(4)
-    await nextTick()
-    expect(mapRefs()).toMatchObject(['1', '2', '3', '4'])
-
-    state.list.shift()
-    await nextTick()
-    expect(mapRefs()).toMatchObject(['2', '3', '4'])
-
-    show.value = !show.value
-    await nextTick()
-
-    expect(mapRefs()).toMatchObject([])
-
-    show.value = !show.value
-    await nextTick()
-    expect(mapRefs()).toMatchObject(['2', '3', '4'])
-  })
-})
diff --git a/test/unit/features/v3/useCssVars.spec.ts b/test/unit/features/v3/useCssVars.spec.ts
deleted file mode 100644
index 76bb6cca6e2..00000000000
--- a/test/unit/features/v3/useCssVars.spec.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import Vue from 'vue'
-import { useCssVars, h, reactive, nextTick } from 'v3'
-
-describe('useCssVars', () => {
-  async function assertCssVars(getApp: (state: any) => any) {
-    const state = reactive({ color: 'red' })
-    const App = getApp(state)
-    const vm = new Vue(App).$mount()
-    await nextTick()
-    expect((vm.$el as HTMLElement).style.getPropertyValue(`--color`)).toBe(
-      `red`
-    )
-
-    state.color = 'green'
-    await nextTick()
-    expect((vm.$el as HTMLElement).style.getPropertyValue(`--color`)).toBe(
-      `green`
-    )
-  }
-
-  test('basic', async () => {
-    await assertCssVars(state => ({
-      setup() {
-        // test receiving render context
-        useCssVars(vm => ({
-          color: vm.color
-        }))
-        return state
-      },
-      render() {
-        return h('div')
-      }
-    }))
-  })
-
-  test('on HOCs', async () => {
-    const Child = {
-      render: () => h('div')
-    }
-
-    await assertCssVars(state => ({
-      setup() {
-        useCssVars(() => state)
-        return () => h(Child)
-      }
-    }))
-  })
-})
diff --git a/test/unit/index.html b/test/unit/index.html
new file mode 100644
index 00000000000..49849e479b0
--- /dev/null
+++ b/test/unit/index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Vue.js unit tests</title>
+    <link rel="stylesheet" type="text/css" href="lib/jasmine.css">
+    <script src="lib/jquery.js"></script>
+    <script src="lib/jasmine.js"></script>
+    <script src="lib/jasmine-html.js"></script>
+    <script src="lib/boot.js"></script>
+    <script src="specs.js"></script>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/test/unit/lib/MIT.LICENSE b/test/unit/lib/MIT.LICENSE
new file mode 100755
index 00000000000..aff8ed47a1a
--- /dev/null
+++ b/test/unit/lib/MIT.LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008-2014 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/test/unit/lib/boot.js b/test/unit/lib/boot.js
new file mode 100755
index 00000000000..ec8baa0aa59
--- /dev/null
+++ b/test/unit/lib/boot.js
@@ -0,0 +1,181 @@
+/**
+ Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
+
+ If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
+
+ The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
+
+ [jasmine-gem]: http://github.com/pivotal/jasmine-gem
+ */
+
+(function() {
+
+  /**
+   * ## Require &amp; Instantiate
+   *
+   * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
+   */
+  window.jasmine = jasmineRequire.core(jasmineRequire);
+
+  /**
+   * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
+   */
+  jasmineRequire.html(jasmine);
+
+  /**
+   * Create the Jasmine environment. This is used to run all specs in a project.
+   */
+  var env = jasmine.getEnv();
+
+  /**
+   * ## The Global Interface
+   *
+   * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
+   */
+  var jasmineInterface = {
+    describe: function(description, specDefinitions) {
+      return env.describe(description, specDefinitions);
+    },
+
+    xdescribe: function(description, specDefinitions) {
+      return env.xdescribe(description, specDefinitions);
+    },
+
+    it: function(desc, func) {
+      return env.it(desc, func);
+    },
+
+    xit: function(desc, func) {
+      return env.xit(desc, func);
+    },
+
+    beforeEach: function(beforeEachFunction) {
+      return env.beforeEach(beforeEachFunction);
+    },
+
+    afterEach: function(afterEachFunction) {
+      return env.afterEach(afterEachFunction);
+    },
+
+    expect: function(actual) {
+      return env.expect(actual);
+    },
+
+    pending: function() {
+      return env.pending();
+    },
+
+    spyOn: function(obj, methodName) {
+      return env.spyOn(obj, methodName);
+    },
+
+    jsApiReporter: new jasmine.JsApiReporter({
+      timer: new jasmine.Timer()
+    })
+  };
+
+  /**
+   * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
+   */
+  if (typeof window == "undefined" && typeof exports == "object") {
+    extend(exports, jasmineInterface);
+  } else {
+    extend(window, jasmineInterface);
+  }
+
+  /**
+   * Expose the interface for adding custom equality testers.
+   */
+  jasmine.addCustomEqualityTester = function(tester) {
+    env.addCustomEqualityTester(tester);
+  };
+
+  /**
+   * Expose the interface for adding custom expectation matchers
+   */
+  jasmine.addMatchers = function(matchers) {
+    return env.addMatchers(matchers);
+  };
+
+  /**
+   * Expose the mock interface for the JavaScript timeout functions
+   */
+  jasmine.clock = function() {
+    return env.clock;
+  };
+
+  /**
+   * ## Runner Parameters
+   *
+   * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
+   */
+
+  var queryString = new jasmine.QueryString({
+    getWindowLocation: function() { return window.location; }
+  });
+
+  var catchingExceptions = queryString.getParam("catch");
+  env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
+
+  /**
+   * ## Reporters
+   * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
+   */
+  var htmlReporter = new jasmine.HtmlReporter({
+    env: env,
+    onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); },
+    getContainer: function() { return document.body; },
+    createElement: function() { return document.createElement.apply(document, arguments); },
+    createTextNode: function() { return document.createTextNode.apply(document, arguments); },
+    timer: new jasmine.Timer()
+  });
+
+  /**
+   * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results  from JavaScript.
+   */
+  env.addReporter(jasmineInterface.jsApiReporter);
+  env.addReporter(htmlReporter);
+
+  /**
+   * Filter which specs will be run by matching the start of the full name against the `spec` query param.
+   */
+  var specFilter = new jasmine.HtmlSpecFilter({
+    filterString: function() { return queryString.getParam("spec"); }
+  });
+
+  env.specFilter = function(spec) {
+    return specFilter.matches(spec.getFullName());
+  };
+
+  /**
+   * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
+   */
+  window.setTimeout = window.setTimeout;
+  window.setInterval = window.setInterval;
+  window.clearTimeout = window.clearTimeout;
+  window.clearInterval = window.clearInterval;
+
+  /**
+   * ## Execution
+   *
+   * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
+   */
+  var currentWindowOnload = window.onload;
+
+  window.onload = function() {
+    if (currentWindowOnload) {
+      currentWindowOnload();
+    }
+    htmlReporter.initialize();
+    env.execute();
+  };
+
+  /**
+   * Helper function for readability above.
+   */
+  function extend(destination, source) {
+    for (var property in source) destination[property] = source[property];
+    return destination;
+  }
+
+}());
diff --git a/test/unit/lib/jasmine-html.js b/test/unit/lib/jasmine-html.js
new file mode 100755
index 00000000000..9d959032e41
--- /dev/null
+++ b/test/unit/lib/jasmine-html.js
@@ -0,0 +1,390 @@
+/*
+Copyright (c) 2008-2014 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+jasmineRequire.html = function(j$) {
+  j$.ResultsNode = jasmineRequire.ResultsNode();
+  j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
+  j$.QueryString = jasmineRequire.QueryString();
+  j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
+};
+
+jasmineRequire.HtmlReporter = function(j$) {
+
+  var noopTimer = {
+    start: function() {},
+    elapsed: function() { return 0; }
+  };
+
+  function HtmlReporter(options) {
+    var env = options.env || {},
+      getContainer = options.getContainer,
+      createElement = options.createElement,
+      createTextNode = options.createTextNode,
+      onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
+      timer = options.timer || noopTimer,
+      results = [],
+      specsExecuted = 0,
+      failureCount = 0,
+      pendingSpecCount = 0,
+      htmlReporterMain,
+      symbols;
+
+    this.initialize = function() {
+      clearPrior();
+      htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'},
+        createDom('div', {className: 'banner'},
+          createDom('a', {className: 'title', href: 'http://jasmine.github.io/', target: '_blank'}),
+          createDom('span', {className: 'version'}, j$.version)
+        ),
+        createDom('ul', {className: 'symbol-summary'}),
+        createDom('div', {className: 'alert'}),
+        createDom('div', {className: 'results'},
+          createDom('div', {className: 'failures'})
+        )
+      );
+      getContainer().appendChild(htmlReporterMain);
+
+      symbols = find('.symbol-summary');
+    };
+
+    var totalSpecsDefined;
+    this.jasmineStarted = function(options) {
+      totalSpecsDefined = options.totalSpecsDefined || 0;
+      timer.start();
+    };
+
+    var summary = createDom('div', {className: 'summary'});
+
+    var topResults = new j$.ResultsNode({}, '', null),
+      currentParent = topResults;
+
+    this.suiteStarted = function(result) {
+      currentParent.addChild(result, 'suite');
+      currentParent = currentParent.last();
+    };
+
+    this.suiteDone = function(result) {
+      if (currentParent == topResults) {
+        return;
+      }
+
+      currentParent = currentParent.parent;
+    };
+
+    this.specStarted = function(result) {
+      currentParent.addChild(result, 'spec');
+    };
+
+    var failures = [];
+    this.specDone = function(result) {
+      if(noExpectations(result) && console && console.error) {
+        console.error('Spec \'' + result.fullName + '\' has no expectations.');
+      }
+
+      if (result.status != 'disabled') {
+        specsExecuted++;
+      }
+
+      symbols.appendChild(createDom('li', {
+          className: noExpectations(result) ? 'empty' : result.status,
+          id: 'spec_' + result.id,
+          title: result.fullName
+        }
+      ));
+
+      if (result.status == 'failed') {
+        failureCount++;
+
+        var failure =
+          createDom('div', {className: 'spec-detail failed'},
+            createDom('div', {className: 'description'},
+              createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName)
+            ),
+            createDom('div', {className: 'messages'})
+          );
+        var messages = failure.childNodes[1];
+
+        for (var i = 0; i < result.failedExpectations.length; i++) {
+          var expectation = result.failedExpectations[i];
+          messages.appendChild(createDom('div', {className: 'result-message'}, expectation.message));
+          messages.appendChild(createDom('div', {className: 'stack-trace'}, expectation.stack));
+        }
+
+        failures.push(failure);
+      }
+
+      if (result.status == 'pending') {
+        pendingSpecCount++;
+      }
+    };
+
+    this.jasmineDone = function() {
+      var banner = find('.banner');
+      banner.appendChild(createDom('span', {className: 'duration'}, 'finished in ' + timer.elapsed() / 1000 + 's'));
+
+      var alert = find('.alert');
+
+      alert.appendChild(createDom('span', { className: 'exceptions' },
+        createDom('label', { className: 'label', 'for': 'raise-exceptions' }, 'raise exceptions'),
+        createDom('input', {
+          className: 'raise',
+          id: 'raise-exceptions',
+          type: 'checkbox'
+        })
+      ));
+      var checkbox = find('#raise-exceptions');
+
+      checkbox.checked = !env.catchingExceptions();
+      checkbox.onclick = onRaiseExceptionsClick;
+
+      if (specsExecuted < totalSpecsDefined) {
+        var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
+        alert.appendChild(
+          createDom('span', {className: 'bar skipped'},
+            createDom('a', {href: '?', title: 'Run all specs'}, skippedMessage)
+          )
+        );
+      }
+      var statusBarMessage = '';
+      var statusBarClassName = 'bar ';
+
+      if (totalSpecsDefined > 0) {
+        statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
+        if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
+        statusBarClassName += (failureCount > 0) ? 'failed' : 'passed';
+      } else {
+        statusBarClassName += 'skipped';
+        statusBarMessage += 'No specs found';
+      }
+
+      alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage));
+
+      var results = find('.results');
+      results.appendChild(summary);
+
+      summaryList(topResults, summary);
+
+      function summaryList(resultsTree, domParent) {
+        var specListNode;
+        for (var i = 0; i < resultsTree.children.length; i++) {
+          var resultNode = resultsTree.children[i];
+          if (resultNode.type == 'suite') {
+            var suiteListNode = createDom('ul', {className: 'suite', id: 'suite-' + resultNode.result.id},
+              createDom('li', {className: 'suite-detail'},
+                createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description)
+              )
+            );
+
+            summaryList(resultNode, suiteListNode);
+            domParent.appendChild(suiteListNode);
+          }
+          if (resultNode.type == 'spec') {
+            if (domParent.getAttribute('class') != 'specs') {
+              specListNode = createDom('ul', {className: 'specs'});
+              domParent.appendChild(specListNode);
+            }
+            var specDescription = resultNode.result.description;
+            if(noExpectations(resultNode.result)) {
+              specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
+            }
+            specListNode.appendChild(
+              createDom('li', {
+                  className: resultNode.result.status,
+                  id: 'spec-' + resultNode.result.id
+                },
+                createDom('a', {href: specHref(resultNode.result)}, specDescription)
+              )
+            );
+          }
+        }
+      }
+
+      if (failures.length) {
+        alert.appendChild(
+          createDom('span', {className: 'menu bar spec-list'},
+            createDom('span', {}, 'Spec List | '),
+            createDom('a', {className: 'failures-menu', href: '#'}, 'Failures')));
+        alert.appendChild(
+          createDom('span', {className: 'menu bar failure-list'},
+            createDom('a', {className: 'spec-list-menu', href: '#'}, 'Spec List'),
+            createDom('span', {}, ' | Failures ')));
+
+        find('.failures-menu').onclick = function() {
+          setMenuModeTo('failure-list');
+        };
+        find('.spec-list-menu').onclick = function() {
+          setMenuModeTo('spec-list');
+        };
+
+        setMenuModeTo('failure-list');
+
+        var failureNode = find('.failures');
+        for (var i = 0; i < failures.length; i++) {
+          failureNode.appendChild(failures[i]);
+        }
+      }
+    };
+
+    return this;
+
+    function find(selector) {
+      return getContainer().querySelector('.jasmine_html-reporter ' + selector);
+    }
+
+    function clearPrior() {
+      // return the reporter
+      var oldReporter = find('');
+      
+      if(oldReporter) {
+        getContainer().removeChild(oldReporter);
+      }
+    }
+
+    function createDom(type, attrs, childrenVarArgs) {
+      var el = createElement(type);
+
+      for (var i = 2; i < arguments.length; i++) {
+        var child = arguments[i];
+
+        if (typeof child === 'string') {
+          el.appendChild(createTextNode(child));
+        } else {
+          if (child) {
+            el.appendChild(child);
+          }
+        }
+      }
+
+      for (var attr in attrs) {
+        if (attr == 'className') {
+          el[attr] = attrs[attr];
+        } else {
+          el.setAttribute(attr, attrs[attr]);
+        }
+      }
+
+      return el;
+    }
+
+    function pluralize(singular, count) {
+      var word = (count == 1 ? singular : singular + 's');
+
+      return '' + count + ' ' + word;
+    }
+
+    function specHref(result) {
+      return '?spec=' + encodeURIComponent(result.fullName);
+    }
+
+    function setMenuModeTo(mode) {
+      htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode);
+    }
+
+    function noExpectations(result) {
+      return (result.failedExpectations.length + result.passedExpectations.length) === 0 &&
+        result.status === 'passed';
+    }
+  }
+
+  return HtmlReporter;
+};
+
+jasmineRequire.HtmlSpecFilter = function() {
+  function HtmlSpecFilter(options) {
+    var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
+    var filterPattern = new RegExp(filterString);
+
+    this.matches = function(specName) {
+      return filterPattern.test(specName);
+    };
+  }
+
+  return HtmlSpecFilter;
+};
+
+jasmineRequire.ResultsNode = function() {
+  function ResultsNode(result, type, parent) {
+    this.result = result;
+    this.type = type;
+    this.parent = parent;
+
+    this.children = [];
+
+    this.addChild = function(result, type) {
+      this.children.push(new ResultsNode(result, type, this));
+    };
+
+    this.last = function() {
+      return this.children[this.children.length - 1];
+    };
+  }
+
+  return ResultsNode;
+};
+
+jasmineRequire.QueryString = function() {
+  function QueryString(options) {
+
+    this.setParam = function(key, value) {
+      var paramMap = queryStringToParamMap();
+      paramMap[key] = value;
+      options.getWindowLocation().search = toQueryString(paramMap);
+    };
+
+    this.getParam = function(key) {
+      return queryStringToParamMap()[key];
+    };
+
+    return this;
+
+    function toQueryString(paramMap) {
+      var qStrPairs = [];
+      for (var prop in paramMap) {
+        qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]));
+      }
+      return '?' + qStrPairs.join('&');
+    }
+
+    function queryStringToParamMap() {
+      var paramStr = options.getWindowLocation().search.substring(1),
+        params = [],
+        paramMap = {};
+
+      if (paramStr.length > 0) {
+        params = paramStr.split('&');
+        for (var i = 0; i < params.length; i++) {
+          var p = params[i].split('=');
+          var value = decodeURIComponent(p[1]);
+          if (value === 'true' || value === 'false') {
+            value = JSON.parse(value);
+          }
+          paramMap[decodeURIComponent(p[0])] = value;
+        }
+      }
+
+      return paramMap;
+    }
+
+  }
+
+  return QueryString;
+};
diff --git a/test/unit/lib/jasmine.css b/test/unit/lib/jasmine.css
new file mode 100755
index 00000000000..c54ff30505c
--- /dev/null
+++ b/test/unit/lib/jasmine.css
@@ -0,0 +1,59 @@
+body { overflow-y: scroll; }
+
+.jasmine_html-reporter { background-color: #eeeeee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
+.jasmine_html-reporter a { text-decoration: none; }
+.jasmine_html-reporter a:hover { text-decoration: underline; }
+.jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; }
+.jasmine_html-reporter .banner, .jasmine_html-reporter .symbol-summary, .jasmine_html-reporter .summary, .jasmine_html-reporter .result-message, .jasmine_html-reporter .spec .description, .jasmine_html-reporter .spec-detail .description, .jasmine_html-reporter .alert .bar, .jasmine_html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; }
+.jasmine_html-reporter .banner { position: relative; }
+.jasmine_html-reporter .banner .title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -webkit-background-size: 100%; -moz-background-size: 100%; -o-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; }
+.jasmine_html-reporter .banner .version { margin-left: 14px; position: relative; top: 6px; }
+.jasmine_html-reporter .banner .duration { position: absolute; right: 14px; top: 6px; }
+.jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; }
+.jasmine_html-reporter .version { color: #aaaaaa; }
+.jasmine_html-reporter .banner { margin-top: 14px; }
+.jasmine_html-reporter .duration { color: #aaaaaa; float: right; }
+.jasmine_html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; }
+.jasmine_html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; }
+.jasmine_html-reporter .symbol-summary li.passed { font-size: 14px; }
+.jasmine_html-reporter .symbol-summary li.passed:before { color: #007069; content: "\02022"; }
+.jasmine_html-reporter .symbol-summary li.failed { line-height: 9px; }
+.jasmine_html-reporter .symbol-summary li.failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; }
+.jasmine_html-reporter .symbol-summary li.disabled { font-size: 14px; }
+.jasmine_html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; }
+.jasmine_html-reporter .symbol-summary li.pending { line-height: 17px; }
+.jasmine_html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; }
+.jasmine_html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
+.jasmine_html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
+.jasmine_html-reporter .bar.failed { background-color: #ca3a11; }
+.jasmine_html-reporter .bar.passed { background-color: #007069; }
+.jasmine_html-reporter .bar.skipped { background-color: #bababa; }
+.jasmine_html-reporter .bar.menu { background-color: #fff; color: #aaaaaa; }
+.jasmine_html-reporter .bar.menu a { color: #333333; }
+.jasmine_html-reporter .bar a { color: white; }
+.jasmine_html-reporter.spec-list .bar.menu.failure-list, .jasmine_html-reporter.spec-list .results .failures { display: none; }
+.jasmine_html-reporter.failure-list .bar.menu.spec-list, .jasmine_html-reporter.failure-list .summary { display: none; }
+.jasmine_html-reporter .running-alert { background-color: #666666; }
+.jasmine_html-reporter .results { margin-top: 14px; }
+.jasmine_html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
+.jasmine_html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
+.jasmine_html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
+.jasmine_html-reporter.showDetails .summary { display: none; }
+.jasmine_html-reporter.showDetails #details { display: block; }
+.jasmine_html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
+.jasmine_html-reporter .summary { margin-top: 14px; }
+.jasmine_html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; }
+.jasmine_html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; }
+.jasmine_html-reporter .summary li.passed a { color: #007069; }
+.jasmine_html-reporter .summary li.failed a { color: #ca3a11; }
+.jasmine_html-reporter .summary li.empty a { color: #ba9d37; }
+.jasmine_html-reporter .summary li.pending a { color: #ba9d37; }
+.jasmine_html-reporter .description + .suite { margin-top: 0; }
+.jasmine_html-reporter .suite { margin-top: 14px; }
+.jasmine_html-reporter .suite a { color: #333333; }
+.jasmine_html-reporter .failures .spec-detail { margin-bottom: 28px; }
+.jasmine_html-reporter .failures .spec-detail .description { background-color: #ca3a11; }
+.jasmine_html-reporter .failures .spec-detail .description a { color: white; }
+.jasmine_html-reporter .result-message { padding-top: 14px; color: #333333; white-space: pre; }
+.jasmine_html-reporter .result-message span.result { display: block; }
+.jasmine_html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
diff --git a/test/unit/lib/jasmine.js b/test/unit/lib/jasmine.js
new file mode 100755
index 00000000000..c943db1aaff
--- /dev/null
+++ b/test/unit/lib/jasmine.js
@@ -0,0 +1,2516 @@
+/*
+Copyright (c) 2008-2014 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+function getJasmineRequireObj() {
+  if (typeof module !== 'undefined' && module.exports) {
+    return exports;
+  } else {
+    window.jasmineRequire = window.jasmineRequire || {};
+    return window.jasmineRequire;
+  }
+}
+
+getJasmineRequireObj().core = function(jRequire) {
+  var j$ = {};
+
+  jRequire.base(j$);
+  j$.util = jRequire.util();
+  j$.Any = jRequire.Any();
+  j$.CallTracker = jRequire.CallTracker();
+  j$.MockDate = jRequire.MockDate();
+  j$.Clock = jRequire.Clock();
+  j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler();
+  j$.Env = jRequire.Env(j$);
+  j$.ExceptionFormatter = jRequire.ExceptionFormatter();
+  j$.Expectation = jRequire.Expectation();
+  j$.buildExpectationResult = jRequire.buildExpectationResult();
+  j$.JsApiReporter = jRequire.JsApiReporter();
+  j$.matchersUtil = jRequire.matchersUtil(j$);
+  j$.ObjectContaining = jRequire.ObjectContaining(j$);
+  j$.pp = jRequire.pp(j$);
+  j$.QueueRunner = jRequire.QueueRunner(j$);
+  j$.ReportDispatcher = jRequire.ReportDispatcher();
+  j$.Spec = jRequire.Spec(j$);
+  j$.SpyStrategy = jRequire.SpyStrategy();
+  j$.Suite = jRequire.Suite();
+  j$.Timer = jRequire.Timer();
+  j$.version = jRequire.version();
+
+  j$.matchers = jRequire.requireMatchers(jRequire, j$);
+
+  return j$;
+};
+
+getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
+  var availableMatchers = [
+      'toBe',
+      'toBeCloseTo',
+      'toBeDefined',
+      'toBeFalsy',
+      'toBeGreaterThan',
+      'toBeLessThan',
+      'toBeNaN',
+      'toBeNull',
+      'toBeTruthy',
+      'toBeUndefined',
+      'toContain',
+      'toEqual',
+      'toHaveBeenCalled',
+      'toHaveBeenCalledWith',
+      'toMatch',
+      'toThrow',
+      'toThrowError'
+    ],
+    matchers = {};
+
+  for (var i = 0; i < availableMatchers.length; i++) {
+    var name = availableMatchers[i];
+    matchers[name] = jRequire[name](j$);
+  }
+
+  return matchers;
+};
+
+getJasmineRequireObj().base = (function (jasmineGlobal) {
+  if (typeof module !== 'undefined' && module.exports) {
+    jasmineGlobal = global;
+  }
+
+  return function(j$) {
+    j$.unimplementedMethod_ = function() {
+      throw new Error('unimplemented method');
+    };
+
+    j$.MAX_PRETTY_PRINT_DEPTH = 40;
+    j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100;
+    j$.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+    j$.getGlobal = function() {
+      return jasmineGlobal;
+    };
+
+    j$.getEnv = function(options) {
+      var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options);
+      //jasmine. singletons in here (setTimeout blah blah).
+      return env;
+    };
+
+    j$.isArray_ = function(value) {
+      return j$.isA_('Array', value);
+    };
+
+    j$.isString_ = function(value) {
+      return j$.isA_('String', value);
+    };
+
+    j$.isNumber_ = function(value) {
+      return j$.isA_('Number', value);
+    };
+
+    j$.isA_ = function(typeName, value) {
+      return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
+    };
+
+    j$.isDomNode = function(obj) {
+      return obj.nodeType > 0;
+    };
+
+    j$.any = function(clazz) {
+      return new j$.Any(clazz);
+    };
+
+    j$.objectContaining = function(sample) {
+      return new j$.ObjectContaining(sample);
+    };
+
+    j$.createSpy = function(name, originalFn) {
+
+      var spyStrategy = new j$.SpyStrategy({
+          name: name,
+          fn: originalFn,
+          getSpy: function() { return spy; }
+        }),
+        callTracker = new j$.CallTracker(),
+        spy = function() {
+          callTracker.track({
+            object: this,
+            args: Array.prototype.slice.apply(arguments)
+          });
+          return spyStrategy.exec.apply(this, arguments);
+        };
+
+      for (var prop in originalFn) {
+        if (prop === 'and' || prop === 'calls') {
+          throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon');
+        }
+
+        spy[prop] = originalFn[prop];
+      }
+
+      spy.and = spyStrategy;
+      spy.calls = callTracker;
+
+      return spy;
+    };
+
+    j$.isSpy = function(putativeSpy) {
+      if (!putativeSpy) {
+        return false;
+      }
+      return putativeSpy.and instanceof j$.SpyStrategy &&
+        putativeSpy.calls instanceof j$.CallTracker;
+    };
+
+    j$.createSpyObj = function(baseName, methodNames) {
+      if (!j$.isArray_(methodNames) || methodNames.length === 0) {
+        throw 'createSpyObj requires a non-empty array of method names to create spies for';
+      }
+      var obj = {};
+      for (var i = 0; i < methodNames.length; i++) {
+        obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]);
+      }
+      return obj;
+    };
+  };
+})(this);
+
+getJasmineRequireObj().util = function() {
+
+  var util = {};
+
+  util.inherit = function(childClass, parentClass) {
+    var Subclass = function() {
+    };
+    Subclass.prototype = parentClass.prototype;
+    childClass.prototype = new Subclass();
+  };
+
+  util.htmlEscape = function(str) {
+    if (!str) {
+      return str;
+    }
+    return str.replace(/&/g, '&amp;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;');
+  };
+
+  util.argsToArray = function(args) {
+    var arrayOfArgs = [];
+    for (var i = 0; i < args.length; i++) {
+      arrayOfArgs.push(args[i]);
+    }
+    return arrayOfArgs;
+  };
+
+  util.isUndefined = function(obj) {
+    return obj === void 0;
+  };
+
+  util.arrayContains = function(array, search) {
+    var i = array.length;
+    while (i--) {
+      if (array[i] == search) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  return util;
+};
+
+getJasmineRequireObj().Spec = function(j$) {
+  function Spec(attrs) {
+    this.expectationFactory = attrs.expectationFactory;
+    this.resultCallback = attrs.resultCallback || function() {};
+    this.id = attrs.id;
+    this.description = attrs.description || '';
+    this.fn = attrs.fn;
+    this.beforeFns = attrs.beforeFns || function() { return []; };
+    this.afterFns = attrs.afterFns || function() { return []; };
+    this.onStart = attrs.onStart || function() {};
+    this.exceptionFormatter = attrs.exceptionFormatter || function() {};
+    this.getSpecName = attrs.getSpecName || function() { return ''; };
+    this.expectationResultFactory = attrs.expectationResultFactory || function() { };
+    this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
+    this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
+
+    if (!this.fn) {
+      this.pend();
+    }
+
+    this.result = {
+      id: this.id,
+      description: this.description,
+      fullName: this.getFullName(),
+      failedExpectations: [],
+      passedExpectations: []
+    };
+  }
+
+  Spec.prototype.addExpectationResult = function(passed, data) {
+    var expectationResult = this.expectationResultFactory(data);
+    if (passed) {
+      this.result.passedExpectations.push(expectationResult);
+    } else {
+      this.result.failedExpectations.push(expectationResult);
+    }
+  };
+
+  Spec.prototype.expect = function(actual) {
+    return this.expectationFactory(actual, this);
+  };
+
+  Spec.prototype.execute = function(onComplete) {
+    var self = this;
+
+    this.onStart(this);
+
+    if (this.markedPending || this.disabled) {
+      complete();
+      return;
+    }
+
+    var allFns = this.beforeFns().concat(this.fn).concat(this.afterFns());
+
+    this.queueRunnerFactory({
+      fns: allFns,
+      onException: onException,
+      onComplete: complete,
+      enforceTimeout: function() { return true; }
+    });
+
+    function onException(e) {
+      if (Spec.isPendingSpecException(e)) {
+        self.pend();
+        return;
+      }
+
+      self.addExpectationResult(false, {
+        matcherName: '',
+        passed: false,
+        expected: '',
+        actual: '',
+        error: e
+      });
+    }
+
+    function complete() {
+      self.result.status = self.status();
+      self.resultCallback(self.result);
+
+      if (onComplete) {
+        onComplete();
+      }
+    }
+  };
+
+  Spec.prototype.disable = function() {
+    this.disabled = true;
+  };
+
+  Spec.prototype.pend = function() {
+    this.markedPending = true;
+  };
+
+  Spec.prototype.status = function() {
+    if (this.disabled) {
+      return 'disabled';
+    }
+
+    if (this.markedPending) {
+      return 'pending';
+    }
+
+    if (this.result.failedExpectations.length > 0) {
+      return 'failed';
+    } else {
+      return 'passed';
+    }
+  };
+
+  Spec.prototype.getFullName = function() {
+    return this.getSpecName(this);
+  };
+
+  Spec.pendingSpecExceptionMessage = '=> marked Pending';
+
+  Spec.isPendingSpecException = function(e) {
+    return !!(e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1);
+  };
+
+  return Spec;
+};
+
+if (typeof window == void 0 && typeof exports == 'object') {
+  exports.Spec = jasmineRequire.Spec;
+}
+
+getJasmineRequireObj().Env = function(j$) {
+  function Env(options) {
+    options = options || {};
+
+    var self = this;
+    var global = options.global || j$.getGlobal();
+
+    var totalSpecsDefined = 0;
+
+    var catchExceptions = true;
+
+    var realSetTimeout = j$.getGlobal().setTimeout;
+    var realClearTimeout = j$.getGlobal().clearTimeout;
+    this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler(), new j$.MockDate(global));
+
+    var runnableLookupTable = {};
+
+    var spies = [];
+
+    var currentSpec = null;
+    var currentSuite = null;
+
+    var reporter = new j$.ReportDispatcher([
+      'jasmineStarted',
+      'jasmineDone',
+      'suiteStarted',
+      'suiteDone',
+      'specStarted',
+      'specDone'
+    ]);
+
+    this.specFilter = function() {
+      return true;
+    };
+
+    var equalityTesters = [];
+
+    var customEqualityTesters = [];
+    this.addCustomEqualityTester = function(tester) {
+      customEqualityTesters.push(tester);
+    };
+
+    j$.Expectation.addCoreMatchers(j$.matchers);
+
+    var nextSpecId = 0;
+    var getNextSpecId = function() {
+      return 'spec' + nextSpecId++;
+    };
+
+    var nextSuiteId = 0;
+    var getNextSuiteId = function() {
+      return 'suite' + nextSuiteId++;
+    };
+
+    var expectationFactory = function(actual, spec) {
+      return j$.Expectation.Factory({
+        util: j$.matchersUtil,
+        customEqualityTesters: customEqualityTesters,
+        actual: actual,
+        addExpectationResult: addExpectationResult
+      });
+
+      function addExpectationResult(passed, result) {
+        return spec.addExpectationResult(passed, result);
+      }
+    };
+
+    var specStarted = function(spec) {
+      currentSpec = spec;
+      reporter.specStarted(spec.result);
+    };
+
+    var beforeFns = function(suite) {
+      return function() {
+        var befores = [];
+        while(suite) {
+          befores = befores.concat(suite.beforeFns);
+          suite = suite.parentSuite;
+        }
+        return befores.reverse();
+      };
+    };
+
+    var afterFns = function(suite) {
+      return function() {
+        var afters = [];
+        while(suite) {
+          afters = afters.concat(suite.afterFns);
+          suite = suite.parentSuite;
+        }
+        return afters;
+      };
+    };
+
+    var getSpecName = function(spec, suite) {
+      return suite.getFullName() + ' ' + spec.description;
+    };
+
+    // TODO: we may just be able to pass in the fn instead of wrapping here
+    var buildExpectationResult = j$.buildExpectationResult,
+        exceptionFormatter = new j$.ExceptionFormatter(),
+        expectationResultFactory = function(attrs) {
+          attrs.messageFormatter = exceptionFormatter.message;
+          attrs.stackFormatter = exceptionFormatter.stack;
+
+          return buildExpectationResult(attrs);
+        };
+
+    // TODO: fix this naming, and here's where the value comes in
+    this.catchExceptions = function(value) {
+      catchExceptions = !!value;
+      return catchExceptions;
+    };
+
+    this.catchingExceptions = function() {
+      return catchExceptions;
+    };
+
+    var maximumSpecCallbackDepth = 20;
+    var currentSpecCallbackDepth = 0;
+
+    function clearStack(fn) {
+      currentSpecCallbackDepth++;
+      if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) {
+        currentSpecCallbackDepth = 0;
+        realSetTimeout(fn, 0);
+      } else {
+        fn();
+      }
+    }
+
+    var catchException = function(e) {
+      return j$.Spec.isPendingSpecException(e) || catchExceptions;
+    };
+
+    var queueRunnerFactory = function(options) {
+      options.catchException = catchException;
+      options.clearStack = options.clearStack || clearStack;
+      options.timer = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout};
+
+      new j$.QueueRunner(options).execute();
+    };
+
+    var topSuite = new j$.Suite({
+      env: this,
+      id: getNextSuiteId(),
+      description: 'Jasmine__TopLevel__Suite',
+      queueRunner: queueRunnerFactory,
+      resultCallback: function() {} // TODO - hook this up
+    });
+    runnableLookupTable[topSuite.id] = topSuite;
+    currentSuite = topSuite;
+
+    this.topSuite = function() {
+      return topSuite;
+    };
+
+    this.execute = function(runnablesToRun) {
+      runnablesToRun = runnablesToRun || [topSuite.id];
+
+      var allFns = [];
+      for(var i = 0; i < runnablesToRun.length; i++) {
+        var runnable = runnableLookupTable[runnablesToRun[i]];
+        allFns.push((function(runnable) { return function(done) { runnable.execute(done); }; })(runnable));
+      }
+
+      reporter.jasmineStarted({
+        totalSpecsDefined: totalSpecsDefined
+      });
+
+      queueRunnerFactory({fns: allFns, onComplete: reporter.jasmineDone});
+    };
+
+    this.addReporter = function(reporterToAdd) {
+      reporter.addReporter(reporterToAdd);
+    };
+
+    this.addMatchers = function(matchersToAdd) {
+      j$.Expectation.addMatchers(matchersToAdd);
+    };
+
+    this.spyOn = function(obj, methodName) {
+      if (j$.util.isUndefined(obj)) {
+        throw new Error('spyOn could not find an object to spy upon for ' + methodName + '()');
+      }
+
+      if (j$.util.isUndefined(obj[methodName])) {
+        throw new Error(methodName + '() method does not exist');
+      }
+
+      if (obj[methodName] && j$.isSpy(obj[methodName])) {
+        //TODO?: should this return the current spy? Downside: may cause user confusion about spy state
+        throw new Error(methodName + ' has already been spied upon');
+      }
+
+      var spy = j$.createSpy(methodName, obj[methodName]);
+
+      spies.push({
+        spy: spy,
+        baseObj: obj,
+        methodName: methodName,
+        originalValue: obj[methodName]
+      });
+
+      obj[methodName] = spy;
+
+      return spy;
+    };
+
+    var suiteFactory = function(description) {
+      var suite = new j$.Suite({
+        env: self,
+        id: getNextSuiteId(),
+        description: description,
+        parentSuite: currentSuite,
+        queueRunner: queueRunnerFactory,
+        onStart: suiteStarted,
+        resultCallback: function(attrs) {
+          reporter.suiteDone(attrs);
+        }
+      });
+
+      runnableLookupTable[suite.id] = suite;
+      return suite;
+    };
+
+    this.describe = function(description, specDefinitions) {
+      var suite = suiteFactory(description);
+
+      var parentSuite = currentSuite;
+      parentSuite.addChild(suite);
+      currentSuite = suite;
+
+      var declarationError = null;
+      try {
+        specDefinitions.call(suite);
+      } catch (e) {
+        declarationError = e;
+      }
+
+      if (declarationError) {
+        this.it('encountered a declaration exception', function() {
+          throw declarationError;
+        });
+      }
+
+      currentSuite = parentSuite;
+
+      return suite;
+    };
+
+    this.xdescribe = function(description, specDefinitions) {
+      var suite = this.describe(description, specDefinitions);
+      suite.disable();
+      return suite;
+    };
+
+    var specFactory = function(description, fn, suite) {
+      totalSpecsDefined++;
+
+      var spec = new j$.Spec({
+        id: getNextSpecId(),
+        beforeFns: beforeFns(suite),
+        afterFns: afterFns(suite),
+        expectationFactory: expectationFactory,
+        exceptionFormatter: exceptionFormatter,
+        resultCallback: specResultCallback,
+        getSpecName: function(spec) {
+          return getSpecName(spec, suite);
+        },
+        onStart: specStarted,
+        description: description,
+        expectationResultFactory: expectationResultFactory,
+        queueRunnerFactory: queueRunnerFactory,
+        fn: fn
+      });
+
+      runnableLookupTable[spec.id] = spec;
+
+      if (!self.specFilter(spec)) {
+        spec.disable();
+      }
+
+      return spec;
+
+      function removeAllSpies() {
+        for (var i = 0; i < spies.length; i++) {
+          var spyEntry = spies[i];
+          spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue;
+        }
+        spies = [];
+      }
+
+      function specResultCallback(result) {
+        removeAllSpies();
+        j$.Expectation.resetMatchers();
+        customEqualityTesters = [];
+        currentSpec = null;
+        reporter.specDone(result);
+      }
+    };
+
+    var suiteStarted = function(suite) {
+      reporter.suiteStarted(suite.result);
+    };
+
+    this.it = function(description, fn) {
+      var spec = specFactory(description, fn, currentSuite);
+      currentSuite.addChild(spec);
+      return spec;
+    };
+
+    this.xit = function(description, fn) {
+      var spec = this.it(description, fn);
+      spec.pend();
+      return spec;
+    };
+
+    this.expect = function(actual) {
+      if (!currentSpec) {
+        throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out');
+      }
+
+      return currentSpec.expect(actual);
+    };
+
+    this.beforeEach = function(beforeEachFunction) {
+      currentSuite.beforeEach(beforeEachFunction);
+    };
+
+    this.afterEach = function(afterEachFunction) {
+      currentSuite.afterEach(afterEachFunction);
+    };
+
+    this.pending = function() {
+      throw j$.Spec.pendingSpecExceptionMessage;
+    };
+  }
+
+  return Env;
+};
+
+getJasmineRequireObj().JsApiReporter = function() {
+
+  var noopTimer = {
+    start: function(){},
+    elapsed: function(){ return 0; }
+  };
+
+  function JsApiReporter(options) {
+    var timer = options.timer || noopTimer,
+        status = 'loaded';
+
+    this.started = false;
+    this.finished = false;
+
+    this.jasmineStarted = function() {
+      this.started = true;
+      status = 'started';
+      timer.start();
+    };
+
+    var executionTime;
+
+    this.jasmineDone = function() {
+      this.finished = true;
+      executionTime = timer.elapsed();
+      status = 'done';
+    };
+
+    this.status = function() {
+      return status;
+    };
+
+    var suites = {};
+
+    this.suiteStarted = function(result) {
+      storeSuite(result);
+    };
+
+    this.suiteDone = function(result) {
+      storeSuite(result);
+    };
+
+    function storeSuite(result) {
+      suites[result.id] = result;
+    }
+
+    this.suites = function() {
+      return suites;
+    };
+
+    var specs = [];
+    this.specStarted = function(result) { };
+
+    this.specDone = function(result) {
+      specs.push(result);
+    };
+
+    this.specResults = function(index, length) {
+      return specs.slice(index, index + length);
+    };
+
+    this.specs = function() {
+      return specs;
+    };
+
+    this.executionTime = function() {
+      return executionTime;
+    };
+
+  }
+
+  return JsApiReporter;
+};
+
+getJasmineRequireObj().Any = function() {
+
+  function Any(expectedObject) {
+    this.expectedObject = expectedObject;
+  }
+
+  Any.prototype.jasmineMatches = function(other) {
+    if (this.expectedObject == String) {
+      return typeof other == 'string' || other instanceof String;
+    }
+
+    if (this.expectedObject == Number) {
+      return typeof other == 'number' || other instanceof Number;
+    }
+
+    if (this.expectedObject == Function) {
+      return typeof other == 'function' || other instanceof Function;
+    }
+
+    if (this.expectedObject == Object) {
+      return typeof other == 'object';
+    }
+    
+    if (this.expectedObject == Boolean) {
+      return typeof other == 'boolean';
+    }
+
+    return other instanceof this.expectedObject;
+  };
+
+  Any.prototype.jasmineToString = function() {
+    return '<jasmine.any(' + this.expectedObject + ')>';
+  };
+
+  return Any;
+};
+
+getJasmineRequireObj().CallTracker = function() {
+
+  function CallTracker() {
+    var calls = [];
+
+    this.track = function(context) {
+      calls.push(context);
+    };
+
+    this.any = function() {
+      return !!calls.length;
+    };
+
+    this.count = function() {
+      return calls.length;
+    };
+
+    this.argsFor = function(index) {
+      var call = calls[index];
+      return call ? call.args : [];
+    };
+
+    this.all = function() {
+      return calls;
+    };
+
+    this.allArgs = function() {
+      var callArgs = [];
+      for(var i = 0; i < calls.length; i++){
+        callArgs.push(calls[i].args);
+      }
+
+      return callArgs;
+    };
+
+    this.first = function() {
+      return calls[0];
+    };
+
+    this.mostRecent = function() {
+      return calls[calls.length - 1];
+    };
+
+    this.reset = function() {
+      calls = [];
+    };
+  }
+
+  return CallTracker;
+};
+
+getJasmineRequireObj().Clock = function() {
+  function Clock(global, delayedFunctionScheduler, mockDate) {
+    var self = this,
+      realTimingFunctions = {
+        setTimeout: global.setTimeout,
+        clearTimeout: global.clearTimeout,
+        setInterval: global.setInterval,
+        clearInterval: global.clearInterval
+      },
+      fakeTimingFunctions = {
+        setTimeout: setTimeout,
+        clearTimeout: clearTimeout,
+        setInterval: setInterval,
+        clearInterval: clearInterval
+      },
+      installed = false,
+      timer;
+
+
+    self.install = function() {
+      replace(global, fakeTimingFunctions);
+      timer = fakeTimingFunctions;
+      installed = true;
+
+      return self;
+    };
+
+    self.uninstall = function() {
+      delayedFunctionScheduler.reset();
+      mockDate.uninstall();
+      replace(global, realTimingFunctions);
+
+      timer = realTimingFunctions;
+      installed = false;
+    };
+
+    self.mockDate = function(initialDate) {
+      mockDate.install(initialDate);
+    };
+
+    self.setTimeout = function(fn, delay, params) {
+      if (legacyIE()) {
+        if (arguments.length > 2) {
+          throw new Error('IE < 9 cannot support extra params to setTimeout without a polyfill');
+        }
+        return timer.setTimeout(fn, delay);
+      }
+      return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]);
+    };
+
+    self.setInterval = function(fn, delay, params) {
+      if (legacyIE()) {
+        if (arguments.length > 2) {
+          throw new Error('IE < 9 cannot support extra params to setInterval without a polyfill');
+        }
+        return timer.setInterval(fn, delay);
+      }
+      return Function.prototype.apply.apply(timer.setInterval, [global, arguments]);
+    };
+
+    self.clearTimeout = function(id) {
+      return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
+    };
+
+    self.clearInterval = function(id) {
+      return Function.prototype.call.apply(timer.clearInterval, [global, id]);
+    };
+
+    self.tick = function(millis) {
+      if (installed) {
+        mockDate.tick(millis);
+        delayedFunctionScheduler.tick(millis);
+      } else {
+        throw new Error('Mock clock is not installed, use jasmine.clock().install()');
+      }
+    };
+
+    return self;
+
+    function legacyIE() {
+      //if these methods are polyfilled, apply will be present
+      return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply;
+    }
+
+    function replace(dest, source) {
+      for (var prop in source) {
+        dest[prop] = source[prop];
+      }
+    }
+
+    function setTimeout(fn, delay) {
+      return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
+    }
+
+    function clearTimeout(id) {
+      return delayedFunctionScheduler.removeFunctionWithId(id);
+    }
+
+    function setInterval(fn, interval) {
+      return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
+    }
+
+    function clearInterval(id) {
+      return delayedFunctionScheduler.removeFunctionWithId(id);
+    }
+
+    function argSlice(argsObj, n) {
+      return Array.prototype.slice.call(argsObj, n);
+    }
+  }
+
+  return Clock;
+};
+
+getJasmineRequireObj().DelayedFunctionScheduler = function() {
+  function DelayedFunctionScheduler() {
+    var self = this;
+    var scheduledLookup = [];
+    var scheduledFunctions = {};
+    var currentTime = 0;
+    var delayedFnCount = 0;
+
+    self.tick = function(millis) {
+      millis = millis || 0;
+      var endTime = currentTime + millis;
+
+      runScheduledFunctions(endTime);
+      currentTime = endTime;
+    };
+
+    self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
+      var f;
+      if (typeof(funcToCall) === 'string') {
+        /* jshint evil: true */
+        f = function() { return eval(funcToCall); };
+        /* jshint evil: false */
+      } else {
+        f = funcToCall;
+      }
+
+      millis = millis || 0;
+      timeoutKey = timeoutKey || ++delayedFnCount;
+      runAtMillis = runAtMillis || (currentTime + millis);
+
+      var funcToSchedule = {
+        runAtMillis: runAtMillis,
+        funcToCall: f,
+        recurring: recurring,
+        params: params,
+        timeoutKey: timeoutKey,
+        millis: millis
+      };
+
+      if (runAtMillis in scheduledFunctions) {
+        scheduledFunctions[runAtMillis].push(funcToSchedule);
+      } else {
+        scheduledFunctions[runAtMillis] = [funcToSchedule];
+        scheduledLookup.push(runAtMillis);
+        scheduledLookup.sort(function (a, b) {
+          return a - b;
+        });
+      }
+
+      return timeoutKey;
+    };
+
+    self.removeFunctionWithId = function(timeoutKey) {
+      for (var runAtMillis in scheduledFunctions) {
+        var funcs = scheduledFunctions[runAtMillis];
+        var i = indexOfFirstToPass(funcs, function (func) {
+          return func.timeoutKey === timeoutKey;
+        });
+
+        if (i > -1) {
+          if (funcs.length === 1) {
+            delete scheduledFunctions[runAtMillis];
+            deleteFromLookup(runAtMillis);
+          } else {
+            funcs.splice(i, 1);
+          }
+
+          // intervals get rescheduled when executed, so there's never more
+          // than a single scheduled function with a given timeoutKey
+          break;
+        }
+      }
+    };
+
+    self.reset = function() {
+      currentTime = 0;
+      scheduledLookup = [];
+      scheduledFunctions = {};
+      delayedFnCount = 0;
+    };
+
+    return self;
+
+    function indexOfFirstToPass(array, testFn) {
+      var index = -1;
+
+      for (var i = 0; i < array.length; ++i) {
+        if (testFn(array[i])) {
+          index = i;
+          break;
+        }
+      }
+
+      return index;
+    }
+
+    function deleteFromLookup(key) {
+      var value = Number(key);
+      var i = indexOfFirstToPass(scheduledLookup, function (millis) {
+        return millis === value;
+      });
+
+      if (i > -1) {
+        scheduledLookup.splice(i, 1);
+      }
+    }
+
+    function reschedule(scheduledFn) {
+      self.scheduleFunction(scheduledFn.funcToCall,
+        scheduledFn.millis,
+        scheduledFn.params,
+        true,
+        scheduledFn.timeoutKey,
+        scheduledFn.runAtMillis + scheduledFn.millis);
+    }
+
+    function runScheduledFunctions(endTime) {
+      if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
+        return;
+      }
+
+      do {
+        currentTime = scheduledLookup.shift();
+
+        var funcsToRun = scheduledFunctions[currentTime];
+        delete scheduledFunctions[currentTime];
+
+        for (var i = 0; i < funcsToRun.length; ++i) {
+          var funcToRun = funcsToRun[i];
+          funcToRun.funcToCall.apply(null, funcToRun.params || []);
+
+          if (funcToRun.recurring) {
+            reschedule(funcToRun);
+          }
+        }
+      } while (scheduledLookup.length > 0 &&
+              // checking first if we're out of time prevents setTimeout(0)
+              // scheduled in a funcToRun from forcing an extra iteration
+                 currentTime !== endTime  &&
+                 scheduledLookup[0] <= endTime);
+    }
+  }
+
+  return DelayedFunctionScheduler;
+};
+
+getJasmineRequireObj().ExceptionFormatter = function() {
+  function ExceptionFormatter() {
+    this.message = function(error) {
+      var message = '';
+
+      if (error.name && error.message) {
+        message += error.name + ': ' + error.message;
+      } else {
+        message += error.toString() + ' thrown';
+      }
+
+      if (error.fileName || error.sourceURL) {
+        message += ' in ' + (error.fileName || error.sourceURL);
+      }
+
+      if (error.line || error.lineNumber) {
+        message += ' (line ' + (error.line || error.lineNumber) + ')';
+      }
+
+      return message;
+    };
+
+    this.stack = function(error) {
+      return error ? error.stack : null;
+    };
+  }
+
+  return ExceptionFormatter;
+};
+
+getJasmineRequireObj().Expectation = function() {
+
+  var matchers = {};
+
+  function Expectation(options) {
+    this.util = options.util || { buildFailureMessage: function() {} };
+    this.customEqualityTesters = options.customEqualityTesters || [];
+    this.actual = options.actual;
+    this.addExpectationResult = options.addExpectationResult || function(){};
+    this.isNot = options.isNot;
+
+    for (var matcherName in matchers) {
+      this[matcherName] = matchers[matcherName];
+    }
+  }
+
+  Expectation.prototype.wrapCompare = function(name, matcherFactory) {
+    return function() {
+      var args = Array.prototype.slice.call(arguments, 0),
+        expected = args.slice(0),
+        message = '';
+
+      args.unshift(this.actual);
+
+      var matcher = matcherFactory(this.util, this.customEqualityTesters),
+          matcherCompare = matcher.compare;
+
+      function defaultNegativeCompare() {
+        var result = matcher.compare.apply(null, args);
+        result.pass = !result.pass;
+        return result;
+      }
+
+      if (this.isNot) {
+        matcherCompare = matcher.negativeCompare || defaultNegativeCompare;
+      }
+
+      var result = matcherCompare.apply(null, args);
+
+      if (!result.pass) {
+        if (!result.message) {
+          args.unshift(this.isNot);
+          args.unshift(name);
+          message = this.util.buildFailureMessage.apply(null, args);
+        } else {
+          if (Object.prototype.toString.apply(result.message) === '[object Function]') {
+            message = result.message();
+          } else {
+            message = result.message;
+          }
+        }
+      }
+
+      if (expected.length == 1) {
+        expected = expected[0];
+      }
+
+      // TODO: how many of these params are needed?
+      this.addExpectationResult(
+        result.pass,
+        {
+          matcherName: name,
+          passed: result.pass,
+          message: message,
+          actual: this.actual,
+          expected: expected // TODO: this may need to be arrayified/sliced
+        }
+      );
+    };
+  };
+
+  Expectation.addCoreMatchers = function(matchers) {
+    var prototype = Expectation.prototype;
+    for (var matcherName in matchers) {
+      var matcher = matchers[matcherName];
+      prototype[matcherName] = prototype.wrapCompare(matcherName, matcher);
+    }
+  };
+
+  Expectation.addMatchers = function(matchersToAdd) {
+    for (var name in matchersToAdd) {
+      var matcher = matchersToAdd[name];
+      matchers[name] = Expectation.prototype.wrapCompare(name, matcher);
+    }
+  };
+
+  Expectation.resetMatchers = function() {
+    for (var name in matchers) {
+      delete matchers[name];
+    }
+  };
+
+  Expectation.Factory = function(options) {
+    options = options || {};
+
+    var expect = new Expectation(options);
+
+    // TODO: this would be nice as its own Object - NegativeExpectation
+    // TODO: copy instead of mutate options
+    options.isNot = true;
+    expect.not = new Expectation(options);
+
+    return expect;
+  };
+
+  return Expectation;
+};
+
+//TODO: expectation result may make more sense as a presentation of an expectation.
+getJasmineRequireObj().buildExpectationResult = function() {
+  function buildExpectationResult(options) {
+    var messageFormatter = options.messageFormatter || function() {},
+      stackFormatter = options.stackFormatter || function() {};
+
+    return {
+      matcherName: options.matcherName,
+      expected: options.expected,
+      actual: options.actual,
+      message: message(),
+      stack: stack(),
+      passed: options.passed
+    };
+
+    function message() {
+      if (options.passed) {
+        return 'Passed.';
+      } else if (options.message) {
+        return options.message;
+      } else if (options.error) {
+        return messageFormatter(options.error);
+      }
+      return '';
+    }
+
+    function stack() {
+      if (options.passed) {
+        return '';
+      }
+
+      var error = options.error;
+      if (!error) {
+        try {
+          throw new Error(message());
+        } catch (e) {
+          error = e;
+        }
+      }
+      return stackFormatter(error);
+    }
+  }
+
+  return buildExpectationResult;
+};
+
+getJasmineRequireObj().MockDate = function() {
+  function MockDate(global) {
+    var self = this;
+    var currentTime = 0;
+
+    if (!global || !global.Date) {
+      self.install = function() {};
+      self.tick = function() {};
+      self.uninstall = function() {};
+      return self;
+    }
+
+    var GlobalDate = global.Date;
+
+    self.install = function(mockDate) {
+      if (mockDate instanceof GlobalDate) {
+        currentTime = mockDate.getTime();
+      } else {
+        currentTime = new GlobalDate().getTime();
+      }
+
+      global.Date = FakeDate;
+    };
+
+    self.tick = function(millis) {
+      millis = millis || 0;
+      currentTime = currentTime + millis;
+    };
+
+    self.uninstall = function() {
+      currentTime = 0;
+      global.Date = GlobalDate;
+    };
+
+    createDateProperties();
+
+    return self;
+
+    function FakeDate() {
+      if (arguments.length === 0) {
+        return new GlobalDate(currentTime);
+      } else {
+        return new GlobalDate(arguments[0], arguments[1], arguments[2],
+          arguments[3], arguments[4], arguments[5], arguments[6]);
+      }
+    }
+
+    function createDateProperties() {
+
+      FakeDate.now = function() {
+        if (GlobalDate.now) {
+          return currentTime;
+        } else {
+          throw new Error('Browser does not support Date.now()');
+        }
+      };
+
+      FakeDate.toSource = GlobalDate.toSource;
+      FakeDate.toString = GlobalDate.toString;
+      FakeDate.parse = GlobalDate.parse;
+      FakeDate.UTC = GlobalDate.UTC;
+    }
+	}
+
+  return MockDate;
+};
+
+getJasmineRequireObj().ObjectContaining = function(j$) {
+
+  function ObjectContaining(sample) {
+    this.sample = sample;
+  }
+
+  ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
+    if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); }
+
+    mismatchKeys = mismatchKeys || [];
+    mismatchValues = mismatchValues || [];
+
+    var hasKey = function(obj, keyName) {
+      return obj !== null && !j$.util.isUndefined(obj[keyName]);
+    };
+
+    for (var property in this.sample) {
+      if (!hasKey(other, property) && hasKey(this.sample, property)) {
+        mismatchKeys.push('expected has key \'' + property + '\', but missing from actual.');
+      }
+      else if (!j$.matchersUtil.equals(other[property], this.sample[property])) {
+        mismatchValues.push('\'' + property + '\' was \'' + (other[property] ? j$.util.htmlEscape(other[property].toString()) : other[property]) + '\' in actual, but was \'' + (this.sample[property] ? j$.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + '\' in expected.');
+      }
+    }
+
+    return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+  };
+
+  ObjectContaining.prototype.jasmineToString = function() {
+    return '<jasmine.objectContaining(' + j$.pp(this.sample) + ')>';
+  };
+
+  return ObjectContaining;
+};
+
+getJasmineRequireObj().pp = function(j$) {
+
+  function PrettyPrinter() {
+    this.ppNestLevel_ = 0;
+    this.seen = [];
+  }
+
+  PrettyPrinter.prototype.format = function(value) {
+    this.ppNestLevel_++;
+    try {
+      if (j$.util.isUndefined(value)) {
+        this.emitScalar('undefined');
+      } else if (value === null) {
+        this.emitScalar('null');
+      } else if (value === 0 && 1/value === -Infinity) {
+        this.emitScalar('-0');
+      } else if (value === j$.getGlobal()) {
+        this.emitScalar('<global>');
+      } else if (value.jasmineToString) {
+        this.emitScalar(value.jasmineToString());
+      } else if (typeof value === 'string') {
+        this.emitString(value);
+      } else if (j$.isSpy(value)) {
+        this.emitScalar('spy on ' + value.and.identity());
+      } else if (value instanceof RegExp) {
+        this.emitScalar(value.toString());
+      } else if (typeof value === 'function') {
+        this.emitScalar('Function');
+      } else if (typeof value.nodeType === 'number') {
+        this.emitScalar('HTMLNode');
+      } else if (value instanceof Date) {
+        this.emitScalar('Date(' + value + ')');
+      } else if (j$.util.arrayContains(this.seen, value)) {
+        this.emitScalar('<circular reference: ' + (j$.isArray_(value) ? 'Array' : 'Object') + '>');
+      } else if (j$.isArray_(value) || j$.isA_('Object', value)) {
+        this.seen.push(value);
+        if (j$.isArray_(value)) {
+          this.emitArray(value);
+        } else {
+          this.emitObject(value);
+        }
+        this.seen.pop();
+      } else {
+        this.emitScalar(value.toString());
+      }
+    } finally {
+      this.ppNestLevel_--;
+    }
+  };
+
+  PrettyPrinter.prototype.iterateObject = function(obj, fn) {
+    for (var property in obj) {
+      if (!Object.prototype.hasOwnProperty.call(obj, property)) { continue; }
+      fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) &&
+          obj.__lookupGetter__(property) !== null) : false);
+    }
+  };
+
+  PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_;
+  PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_;
+  PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_;
+  PrettyPrinter.prototype.emitString = j$.unimplementedMethod_;
+
+  function StringPrettyPrinter() {
+    PrettyPrinter.call(this);
+
+    this.string = '';
+  }
+
+  j$.util.inherit(StringPrettyPrinter, PrettyPrinter);
+
+  StringPrettyPrinter.prototype.emitScalar = function(value) {
+    this.append(value);
+  };
+
+  StringPrettyPrinter.prototype.emitString = function(value) {
+    this.append('\'' + value + '\'');
+  };
+
+  StringPrettyPrinter.prototype.emitArray = function(array) {
+    if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
+      this.append('Array');
+      return;
+    }
+    var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
+    this.append('[ ');
+    for (var i = 0; i < length; i++) {
+      if (i > 0) {
+        this.append(', ');
+      }
+      this.format(array[i]);
+    }
+    if(array.length > length){
+      this.append(', ...');
+    }
+    this.append(' ]');
+  };
+
+  StringPrettyPrinter.prototype.emitObject = function(obj) {
+    if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
+      this.append('Object');
+      return;
+    }
+
+    var self = this;
+    this.append('{ ');
+    var first = true;
+
+    this.iterateObject(obj, function(property, isGetter) {
+      if (first) {
+        first = false;
+      } else {
+        self.append(', ');
+      }
+
+      self.append(property);
+      self.append(': ');
+      if (isGetter) {
+        self.append('<getter>');
+      } else {
+        self.format(obj[property]);
+      }
+    });
+
+    this.append(' }');
+  };
+
+  StringPrettyPrinter.prototype.append = function(value) {
+    this.string += value;
+  };
+
+  return function(value) {
+    var stringPrettyPrinter = new StringPrettyPrinter();
+    stringPrettyPrinter.format(value);
+    return stringPrettyPrinter.string;
+  };
+};
+
+getJasmineRequireObj().QueueRunner = function(j$) {
+
+  function once(fn) {
+    var called = false;
+    return function() {
+      if (!called) {
+        called = true;
+        fn();
+      }
+    };
+  }
+
+  function QueueRunner(attrs) {
+    this.fns = attrs.fns || [];
+    this.onComplete = attrs.onComplete || function() {};
+    this.clearStack = attrs.clearStack || function(fn) {fn();};
+    this.onException = attrs.onException || function() {};
+    this.catchException = attrs.catchException || function() { return true; };
+    this.enforceTimeout = attrs.enforceTimeout || function() { return false; };
+    this.userContext = {};
+    this.timer = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout};
+  }
+
+  QueueRunner.prototype.execute = function() {
+    this.run(this.fns, 0);
+  };
+
+  QueueRunner.prototype.run = function(fns, recursiveIndex) {
+    var length = fns.length,
+        self = this,
+        iterativeIndex;
+
+    for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
+      var fn = fns[iterativeIndex];
+      if (fn.length > 0) {
+        return attemptAsync(fn);
+      } else {
+        attemptSync(fn);
+      }
+    }
+
+    var runnerDone = iterativeIndex >= length;
+
+    if (runnerDone) {
+      this.clearStack(this.onComplete);
+    }
+
+    function attemptSync(fn) {
+      try {
+        fn.call(self.userContext);
+      } catch (e) {
+        handleException(e);
+      }
+    }
+
+    function attemptAsync(fn) {
+      var clearTimeout = function () {
+          Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeoutId]]);
+        },
+        next = once(function () {
+          clearTimeout(timeoutId);
+          self.run(fns, iterativeIndex + 1);
+        }),
+        timeoutId;
+
+      if (self.enforceTimeout()) {
+        timeoutId = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() {
+          self.onException(new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'));
+          next();
+        }, j$.DEFAULT_TIMEOUT_INTERVAL]]);
+      }
+
+      try {
+        fn.call(self.userContext, next);
+      } catch (e) {
+        handleException(e);
+        next();
+      }
+    }
+
+    function handleException(e) {
+      self.onException(e);
+      if (!self.catchException(e)) {
+        //TODO: set a var when we catch an exception and
+        //use a finally block to close the loop in a nice way..
+        throw e;
+      }
+    }
+  };
+
+  return QueueRunner;
+};
+
+getJasmineRequireObj().ReportDispatcher = function() {
+  function ReportDispatcher(methods) {
+
+    var dispatchedMethods = methods || [];
+
+    for (var i = 0; i < dispatchedMethods.length; i++) {
+      var method = dispatchedMethods[i];
+      this[method] = (function(m) {
+        return function() {
+          dispatch(m, arguments);
+        };
+      }(method));
+    }
+
+    var reporters = [];
+
+    this.addReporter = function(reporter) {
+      reporters.push(reporter);
+    };
+
+    return this;
+
+    function dispatch(method, args) {
+      for (var i = 0; i < reporters.length; i++) {
+        var reporter = reporters[i];
+        if (reporter[method]) {
+          reporter[method].apply(reporter, args);
+        }
+      }
+    }
+  }
+
+  return ReportDispatcher;
+};
+
+
+getJasmineRequireObj().SpyStrategy = function() {
+
+  function SpyStrategy(options) {
+    options = options || {};
+
+    var identity = options.name || 'unknown',
+        originalFn = options.fn || function() {},
+        getSpy = options.getSpy || function() {},
+        plan = function() {};
+
+    this.identity = function() {
+      return identity;
+    };
+
+    this.exec = function() {
+      return plan.apply(this, arguments);
+    };
+
+    this.callThrough = function() {
+      plan = originalFn;
+      return getSpy();
+    };
+
+    this.returnValue = function(value) {
+      plan = function() {
+        return value;
+      };
+      return getSpy();
+    };
+
+    this.throwError = function(something) {
+      var error = (something instanceof Error) ? something : new Error(something);
+      plan = function() {
+        throw error;
+      };
+      return getSpy();
+    };
+
+    this.callFake = function(fn) {
+      plan = fn;
+      return getSpy();
+    };
+
+    this.stub = function(fn) {
+      plan = function() {};
+      return getSpy();
+    };
+  }
+
+  return SpyStrategy;
+};
+
+getJasmineRequireObj().Suite = function() {
+  function Suite(attrs) {
+    this.env = attrs.env;
+    this.id = attrs.id;
+    this.parentSuite = attrs.parentSuite;
+    this.description = attrs.description;
+    this.onStart = attrs.onStart || function() {};
+    this.resultCallback = attrs.resultCallback || function() {};
+    this.clearStack = attrs.clearStack || function(fn) {fn();};
+
+    this.beforeFns = [];
+    this.afterFns = [];
+    this.queueRunner = attrs.queueRunner || function() {};
+    this.disabled = false;
+
+    this.children = [];
+
+    this.result = {
+      id: this.id,
+      status: this.disabled ? 'disabled' : '',
+      description: this.description,
+      fullName: this.getFullName()
+    };
+  }
+
+  Suite.prototype.getFullName = function() {
+    var fullName = this.description;
+    for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
+      if (parentSuite.parentSuite) {
+        fullName = parentSuite.description + ' ' + fullName;
+      }
+    }
+    return fullName;
+  };
+
+  Suite.prototype.disable = function() {
+    this.disabled = true;
+  };
+
+  Suite.prototype.beforeEach = function(fn) {
+    this.beforeFns.unshift(fn);
+  };
+
+  Suite.prototype.afterEach = function(fn) {
+    this.afterFns.unshift(fn);
+  };
+
+  Suite.prototype.addChild = function(child) {
+    this.children.push(child);
+  };
+
+  Suite.prototype.execute = function(onComplete) {
+    var self = this;
+    if (this.disabled) {
+      complete();
+      return;
+    }
+
+    var allFns = [];
+
+    for (var i = 0; i < this.children.length; i++) {
+      allFns.push(wrapChildAsAsync(this.children[i]));
+    }
+
+    this.onStart(this);
+
+    this.queueRunner({
+      fns: allFns,
+      onComplete: complete
+    });
+
+    function complete() {
+      self.resultCallback(self.result);
+
+      if (onComplete) {
+        onComplete();
+      }
+    }
+
+    function wrapChildAsAsync(child) {
+      return function(done) { child.execute(done); };
+    }
+  };
+
+  return Suite;
+};
+
+if (typeof window == void 0 && typeof exports == 'object') {
+  exports.Suite = jasmineRequire.Suite;
+}
+
+getJasmineRequireObj().Timer = function() {
+  var defaultNow = (function(Date) {
+    return function() { return new Date().getTime(); };
+  })(Date);
+
+  function Timer(options) {
+    options = options || {};
+
+    var now = options.now || defaultNow,
+      startTime;
+
+    this.start = function() {
+      startTime = now();
+    };
+
+    this.elapsed = function() {
+      return now() - startTime;
+    };
+  }
+
+  return Timer;
+};
+
+getJasmineRequireObj().matchersUtil = function(j$) {
+  // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter?
+
+  return {
+    equals: function(a, b, customTesters) {
+      customTesters = customTesters || [];
+
+      return eq(a, b, [], [], customTesters);
+    },
+
+    contains: function(haystack, needle, customTesters) {
+      customTesters = customTesters || [];
+
+      if (Object.prototype.toString.apply(haystack) === '[object Array]') {
+        for (var i = 0; i < haystack.length; i++) {
+          if (eq(haystack[i], needle, [], [], customTesters)) {
+            return true;
+          }
+        }
+        return false;
+      }
+      return !!haystack && haystack.indexOf(needle) >= 0;
+    },
+
+    buildFailureMessage: function() {
+      var args = Array.prototype.slice.call(arguments, 0),
+        matcherName = args[0],
+        isNot = args[1],
+        actual = args[2],
+        expected = args.slice(3),
+        englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
+
+      var message = 'Expected ' +
+        j$.pp(actual) +
+        (isNot ? ' not ' : ' ') +
+        englishyPredicate;
+
+      if (expected.length > 0) {
+        for (var i = 0; i < expected.length; i++) {
+          if (i > 0) {
+            message += ',';
+          }
+          message += ' ' + j$.pp(expected[i]);
+        }
+      }
+
+      return message + '.';
+    }
+  };
+
+  // Equality function lovingly adapted from isEqual in
+  //   [Underscore](http://underscorejs.org)
+  function eq(a, b, aStack, bStack, customTesters) {
+    var result = true;
+
+    for (var i = 0; i < customTesters.length; i++) {
+      var customTesterResult = customTesters[i](a, b);
+      if (!j$.util.isUndefined(customTesterResult)) {
+        return customTesterResult;
+      }
+    }
+
+    if (a instanceof j$.Any) {
+      result = a.jasmineMatches(b);
+      if (result) {
+        return true;
+      }
+    }
+
+    if (b instanceof j$.Any) {
+      result = b.jasmineMatches(a);
+      if (result) {
+        return true;
+      }
+    }
+
+    if (b instanceof j$.ObjectContaining) {
+      result = b.jasmineMatches(a);
+      if (result) {
+        return true;
+      }
+    }
+
+    if (a instanceof Error && b instanceof Error) {
+      return a.message == b.message;
+    }
+
+    // Identical objects are equal. `0 === -0`, but they aren't identical.
+    // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
+    if (a === b) { return a !== 0 || 1 / a == 1 / b; }
+    // A strict comparison is necessary because `null == undefined`.
+    if (a === null || b === null) { return a === b; }
+    var className = Object.prototype.toString.call(a);
+    if (className != Object.prototype.toString.call(b)) { return false; }
+    switch (className) {
+      // Strings, numbers, dates, and booleans are compared by value.
+      case '[object String]':
+        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+        // equivalent to `new String("5")`.
+        return a == String(b);
+      case '[object Number]':
+        // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+        // other numeric values.
+        return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
+      case '[object Date]':
+      case '[object Boolean]':
+        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+        // millisecond representations. Note that invalid dates with millisecond representations
+        // of `NaN` are not equivalent.
+        return +a == +b;
+      // RegExps are compared by their source patterns and flags.
+      case '[object RegExp]':
+        return a.source == b.source &&
+          a.global == b.global &&
+          a.multiline == b.multiline &&
+          a.ignoreCase == b.ignoreCase;
+    }
+    if (typeof a != 'object' || typeof b != 'object') { return false; }
+    // Assume equality for cyclic structures. The algorithm for detecting cyclic
+    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+    var length = aStack.length;
+    while (length--) {
+      // Linear search. Performance is inversely proportional to the number of
+      // unique nested structures.
+      if (aStack[length] == a) { return bStack[length] == b; }
+    }
+    // Add the first object to the stack of traversed objects.
+    aStack.push(a);
+    bStack.push(b);
+    var size = 0;
+    // Recursively compare objects and arrays.
+    if (className == '[object Array]') {
+      // Compare array lengths to determine if a deep comparison is necessary.
+      size = a.length;
+      result = size == b.length;
+      if (result) {
+        // Deep compare the contents, ignoring non-numeric properties.
+        while (size--) {
+          if (!(result = eq(a[size], b[size], aStack, bStack, customTesters))) { break; }
+        }
+      }
+    } else {
+      // Objects with different constructors are not equivalent, but `Object`s
+      // from different frames are.
+      var aCtor = a.constructor, bCtor = b.constructor;
+      if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) &&
+        isFunction(bCtor) && (bCtor instanceof bCtor))) {
+        return false;
+      }
+      // Deep compare objects.
+      for (var key in a) {
+        if (has(a, key)) {
+          // Count the expected number of properties.
+          size++;
+          // Deep compare each member.
+          if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; }
+        }
+      }
+      // Ensure that both objects contain the same number of properties.
+      if (result) {
+        for (key in b) {
+          if (has(b, key) && !(size--)) { break; }
+        }
+        result = !size;
+      }
+    }
+    // Remove the first object from the stack of traversed objects.
+    aStack.pop();
+    bStack.pop();
+
+    return result;
+
+    function has(obj, key) {
+      return obj.hasOwnProperty(key);
+    }
+
+    function isFunction(obj) {
+      return typeof obj === 'function';
+    }
+  }
+};
+
+getJasmineRequireObj().toBe = function() {
+  function toBe() {
+    return {
+      compare: function(actual, expected) {
+        return {
+          pass: actual === expected
+        };
+      }
+    };
+  }
+
+  return toBe;
+};
+
+getJasmineRequireObj().toBeCloseTo = function() {
+
+  function toBeCloseTo() {
+    return {
+      compare: function(actual, expected, precision) {
+        if (precision !== 0) {
+          precision = precision || 2;
+        }
+
+        return {
+          pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
+        };
+      }
+    };
+  }
+
+  return toBeCloseTo;
+};
+
+getJasmineRequireObj().toBeDefined = function() {
+  function toBeDefined() {
+    return {
+      compare: function(actual) {
+        return {
+          pass: (void 0 !== actual)
+        };
+      }
+    };
+  }
+
+  return toBeDefined;
+};
+
+getJasmineRequireObj().toBeFalsy = function() {
+  function toBeFalsy() {
+    return {
+      compare: function(actual) {
+        return {
+          pass: !!!actual
+        };
+      }
+    };
+  }
+
+  return toBeFalsy;
+};
+
+getJasmineRequireObj().toBeGreaterThan = function() {
+
+  function toBeGreaterThan() {
+    return {
+      compare: function(actual, expected) {
+        return {
+          pass: actual > expected
+        };
+      }
+    };
+  }
+
+  return toBeGreaterThan;
+};
+
+
+getJasmineRequireObj().toBeLessThan = function() {
+  function toBeLessThan() {
+    return {
+
+      compare: function(actual, expected) {
+        return {
+          pass: actual < expected
+        };
+      }
+    };
+  }
+
+  return toBeLessThan;
+};
+getJasmineRequireObj().toBeNaN = function(j$) {
+
+  function toBeNaN() {
+    return {
+      compare: function(actual) {
+        var result = {
+          pass: (actual !== actual)
+        };
+
+        if (result.pass) {
+          result.message = 'Expected actual not to be NaN.';
+        } else {
+          result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; };
+        }
+
+        return result;
+      }
+    };
+  }
+
+  return toBeNaN;
+};
+
+getJasmineRequireObj().toBeNull = function() {
+
+  function toBeNull() {
+    return {
+      compare: function(actual) {
+        return {
+          pass: actual === null
+        };
+      }
+    };
+  }
+
+  return toBeNull;
+};
+
+getJasmineRequireObj().toBeTruthy = function() {
+
+  function toBeTruthy() {
+    return {
+      compare: function(actual) {
+        return {
+          pass: !!actual
+        };
+      }
+    };
+  }
+
+  return toBeTruthy;
+};
+
+getJasmineRequireObj().toBeUndefined = function() {
+
+  function toBeUndefined() {
+    return {
+      compare: function(actual) {
+        return {
+          pass: void 0 === actual
+        };
+      }
+    };
+  }
+
+  return toBeUndefined;
+};
+
+getJasmineRequireObj().toContain = function() {
+  function toContain(util, customEqualityTesters) {
+    customEqualityTesters = customEqualityTesters || [];
+
+    return {
+      compare: function(actual, expected) {
+
+        return {
+          pass: util.contains(actual, expected, customEqualityTesters)
+        };
+      }
+    };
+  }
+
+  return toContain;
+};
+
+getJasmineRequireObj().toEqual = function() {
+
+  function toEqual(util, customEqualityTesters) {
+    customEqualityTesters = customEqualityTesters || [];
+
+    return {
+      compare: function(actual, expected) {
+        var result = {
+          pass: false
+        };
+
+        result.pass = util.equals(actual, expected, customEqualityTesters);
+
+        return result;
+      }
+    };
+  }
+
+  return toEqual;
+};
+
+getJasmineRequireObj().toHaveBeenCalled = function(j$) {
+
+  function toHaveBeenCalled() {
+    return {
+      compare: function(actual) {
+        var result = {};
+
+        if (!j$.isSpy(actual)) {
+          throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
+        }
+
+        if (arguments.length > 1) {
+          throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
+        }
+
+        result.pass = actual.calls.any();
+
+        result.message = result.pass ?
+          'Expected spy ' + actual.and.identity() + ' not to have been called.' :
+          'Expected spy ' + actual.and.identity() + ' to have been called.';
+
+        return result;
+      }
+    };
+  }
+
+  return toHaveBeenCalled;
+};
+
+getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
+
+  function toHaveBeenCalledWith(util, customEqualityTesters) {
+    return {
+      compare: function() {
+        var args = Array.prototype.slice.call(arguments, 0),
+          actual = args[0],
+          expectedArgs = args.slice(1),
+          result = { pass: false };
+
+        if (!j$.isSpy(actual)) {
+          throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.');
+        }
+
+        if (!actual.calls.any()) {
+          result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but it was never called.'; };
+          return result;
+        }
+
+        if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) {
+          result.pass = true;
+          result.message = function() { return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + j$.pp(expectedArgs) + ' but it was.'; };
+        } else {
+          result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but actual calls were ' + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + '.'; };
+        }
+
+        return result;
+      }
+    };
+  }
+
+  return toHaveBeenCalledWith;
+};
+
+getJasmineRequireObj().toMatch = function() {
+
+  function toMatch() {
+    return {
+      compare: function(actual, expected) {
+        var regexp = new RegExp(expected);
+
+        return {
+          pass: regexp.test(actual)
+        };
+      }
+    };
+  }
+
+  return toMatch;
+};
+
+getJasmineRequireObj().toThrow = function(j$) {
+
+  function toThrow(util) {
+    return {
+      compare: function(actual, expected) {
+        var result = { pass: false },
+          threw = false,
+          thrown;
+
+        if (typeof actual != 'function') {
+          throw new Error('Actual is not a Function');
+        }
+
+        try {
+          actual();
+        } catch (e) {
+          threw = true;
+          thrown = e;
+        }
+
+        if (!threw) {
+          result.message = 'Expected function to throw an exception.';
+          return result;
+        }
+
+        if (arguments.length == 1) {
+          result.pass = true;
+          result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; };
+
+          return result;
+        }
+
+        if (util.equals(thrown, expected)) {
+          result.pass = true;
+          result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; };
+        } else {
+          result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' +  j$.pp(thrown) + '.'; };
+        }
+
+        return result;
+      }
+    };
+  }
+
+  return toThrow;
+};
+
+getJasmineRequireObj().toThrowError = function(j$) {
+  function toThrowError (util) {
+    return {
+      compare: function(actual) {
+        var threw = false,
+          pass = {pass: true},
+          fail = {pass: false},
+          thrown,
+          errorType,
+          message,
+          regexp,
+          name,
+          constructorName;
+
+        if (typeof actual != 'function') {
+          throw new Error('Actual is not a Function');
+        }
+
+        extractExpectedParams.apply(null, arguments);
+
+        try {
+          actual();
+        } catch (e) {
+          threw = true;
+          thrown = e;
+        }
+
+        if (!threw) {
+          fail.message = 'Expected function to throw an Error.';
+          return fail;
+        }
+
+        if (!(thrown instanceof Error)) {
+          fail.message = function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; };
+          return fail;
+        }
+
+        if (arguments.length == 1) {
+          pass.message = 'Expected function not to throw an Error, but it threw ' + fnNameFor(thrown) + '.';
+          return pass;
+        }
+
+        if (errorType) {
+          name = fnNameFor(errorType);
+          constructorName = fnNameFor(thrown.constructor);
+        }
+
+        if (errorType && message) {
+          if (thrown.constructor == errorType && util.equals(thrown.message, message)) {
+            pass.message = function() { return 'Expected function not to throw ' + name + ' with message ' + j$.pp(message) + '.'; };
+            return pass;
+          } else {
+            fail.message = function() { return 'Expected function to throw ' + name + ' with message ' + j$.pp(message) +
+              ', but it threw ' + constructorName + ' with message ' + j$.pp(thrown.message) + '.'; };
+            return fail;
+          }
+        }
+
+        if (errorType && regexp) {
+          if (thrown.constructor == errorType && regexp.test(thrown.message)) {
+            pass.message = function() { return 'Expected function not to throw ' + name + ' with message matching ' + j$.pp(regexp) + '.'; };
+            return pass;
+          } else {
+            fail.message = function() { return 'Expected function to throw ' + name + ' with message matching ' + j$.pp(regexp) +
+              ', but it threw ' + constructorName + ' with message ' + j$.pp(thrown.message) + '.'; };
+            return fail;
+          }
+        }
+
+        if (errorType) {
+          if (thrown.constructor == errorType) {
+            pass.message = 'Expected function not to throw ' + name + '.';
+            return pass;
+          } else {
+            fail.message = 'Expected function to throw ' + name + ', but it threw ' + constructorName + '.';
+            return fail;
+          }
+        }
+
+        if (message) {
+          if (thrown.message == message) {
+            pass.message = function() { return 'Expected function not to throw an exception with message ' + j$.pp(message) + '.'; };
+            return pass;
+          } else {
+            fail.message = function() { return 'Expected function to throw an exception with message ' + j$.pp(message) +
+              ', but it threw an exception with message ' + j$.pp(thrown.message) + '.'; };
+            return fail;
+          }
+        }
+
+        if (regexp) {
+          if (regexp.test(thrown.message)) {
+            pass.message = function() { return 'Expected function not to throw an exception with a message matching ' + j$.pp(regexp) + '.'; };
+            return pass;
+          } else {
+            fail.message = function() { return 'Expected function to throw an exception with a message matching ' + j$.pp(regexp) +
+              ', but it threw an exception with message ' + j$.pp(thrown.message) + '.'; };
+            return fail;
+          }
+        }
+
+        function fnNameFor(func) {
+            return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1];
+        }
+
+        function extractExpectedParams() {
+          if (arguments.length == 1) {
+            return;
+          }
+
+          if (arguments.length == 2) {
+            var expected = arguments[1];
+
+            if (expected instanceof RegExp) {
+              regexp = expected;
+            } else if (typeof expected == 'string') {
+              message = expected;
+            } else if (checkForAnErrorType(expected)) {
+              errorType = expected;
+            }
+
+            if (!(errorType || message || regexp)) {
+              throw new Error('Expected is not an Error, string, or RegExp.');
+            }
+          } else {
+            if (checkForAnErrorType(arguments[1])) {
+              errorType = arguments[1];
+            } else {
+              throw new Error('Expected error type is not an Error.');
+            }
+
+            if (arguments[2] instanceof RegExp) {
+              regexp = arguments[2];
+            } else if (typeof arguments[2] == 'string') {
+              message = arguments[2];
+            } else {
+              throw new Error('Expected error message is not a string or RegExp.');
+            }
+          }
+        }
+
+        function checkForAnErrorType(type) {
+          if (typeof type !== 'function') {
+            return false;
+          }
+
+          var Surrogate = function() {};
+          Surrogate.prototype = type.prototype;
+          return (new Surrogate()) instanceof Error;
+        }
+      }
+    };
+  }
+
+  return toThrowError;
+};
+
+getJasmineRequireObj().version = function() {
+  return '2.0.1';
+};
diff --git a/test/unit/lib/jquery.js b/test/unit/lib/jquery.js
new file mode 100644
index 00000000000..25714ed29ab
--- /dev/null
+++ b/test/unit/lib/jquery.js
@@ -0,0 +1,4 @@
+/*! jQuery v2.1.3 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.3",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)+1>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=hb(),z=hb(),A=hb(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},eb=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fb){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function gb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+rb(o[l]);w=ab.test(a)&&pb(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function hb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ib(a){return a[u]=!0,a}function jb(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function kb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function lb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function nb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function ob(a){return ib(function(b){return b=+b,ib(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pb(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=gb.support={},f=gb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=gb.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",eb,!1):e.attachEvent&&e.attachEvent("onunload",eb)),p=!f(g),c.attributes=jb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=jb(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=jb(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(jb(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\f]' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),jb(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&jb(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return lb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?lb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},gb.matches=function(a,b){return gb(a,null,null,b)},gb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return gb(b,n,null,[a]).length>0},gb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},gb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},gb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},gb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=gb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=gb.selectors={cacheLength:50,createPseudo:ib,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||gb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&gb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=gb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||gb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ib(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ib(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ib(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ib(function(a){return function(b){return gb(a,b).length>0}}),contains:ib(function(a){return a=a.replace(cb,db),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ib(function(a){return W.test(a||"")||gb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:ob(function(){return[0]}),last:ob(function(a,b){return[b-1]}),eq:ob(function(a,b,c){return[0>c?c+b:c]}),even:ob(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:ob(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:ob(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:ob(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=mb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=nb(b);function qb(){}qb.prototype=d.filters=d.pseudos,d.setFilters=new qb,g=gb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?gb.error(a):z(a,i).slice(0)};function rb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function sb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function tb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ub(a,b,c){for(var d=0,e=b.length;e>d;d++)gb(a,b[d],c);return c}function vb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wb(a,b,c,d,e,f){return d&&!d[u]&&(d=wb(d)),e&&!e[u]&&(e=wb(e,f)),ib(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ub(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:vb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=vb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=vb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sb(function(a){return a===b},h,!0),l=sb(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sb(tb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wb(i>1&&tb(m),i>1&&rb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xb(a.slice(i,e)),f>e&&xb(a=a.slice(e)),f>e&&rb(a))}m.push(c)}return tb(m)}function yb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=vb(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&gb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ib(f):f}return h=gb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,yb(e,d)),f.selector=a}return f},i=gb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&pb(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&rb(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&pb(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=jb(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),jb(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||kb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&jb(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||kb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),jb(function(a){return null==a.getAttribute("disabled")})||kb(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),gb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+K.uid++}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)
+},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button;return null==a.pageX&&null!=b.clientX&&(c=a.target.ownerDocument||l,d=c.documentElement,e=c.body,a.pageX=b.clientX+(d&&d.scrollLeft||e&&e.scrollLeft||0)-(d&&d.clientLeft||e&&e.clientLeft||0),a.pageY=b.clientY+(d&&d.scrollTop||e&&e.scrollTop||0)-(d&&d.clientTop||e&&e.clientTop||0)),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=W.test(e)?this.mouseHooks:V.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=l),3===a.target.nodeType&&(a.target=a.target.parentNode),g.filter?g.filter(a,f):a},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==_()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===_()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&n.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?Z:$):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:$,isPropagationStopped:$,isImmediatePropagationStopped:$,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=Z,a&&a.preventDefault&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=Z,a&&a.stopPropagation&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=Z,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=L.access(d,b);e||d.addEventListener(a,c,!0),L.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=L.access(d,b)-1;e?L.access(d,b,e):(d.removeEventListener(a,c,!0),L.remove(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(g in a)this.on(g,b,c,a[g],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=$;else if(!d)return this;return 1===e&&(f=d,d=function(a){return n().off(a),f.apply(this,arguments)},d.guid=f.guid||(f.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=$),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var ab=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ib={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1></$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=qb[0].contentDocument,b.write(),b.close(),c=sb(a,b),qb.detach()),rb[a]=c),c}var ub=/^margin/,vb=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wb=function(b){return b.ownerDocument.defaultView.opener?b.ownerDocument.defaultView.getComputedStyle(b,null):a.getComputedStyle(b,null)};function xb(a,b,c){var d,e,f,g,h=a.style;return c=c||wb(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),vb.test(g)&&ub.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function yb(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),f.removeChild(c),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var zb=/^(none|table(?!-c[ea]).+)/,Ab=new RegExp("^("+Q+")(.*)$","i"),Bb=new RegExp("^([+-])=("+Q+")","i"),Cb={position:"absolute",visibility:"hidden",display:"block"},Db={letterSpacing:"0",fontWeight:"400"},Eb=["Webkit","O","Moz","ms"];function Fb(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Eb.length;while(e--)if(b=Eb[e]+c,b in a)return b;return d}function Gb(a,b,c){var d=Ab.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Hb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ib(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wb(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xb(a,b,f),(0>e||null==e)&&(e=a.style[b]),vb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Hb(a,b,c||(g?"border":"content"),d,f)+"px"}function Jb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",tb(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fb(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Bb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fb(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xb(a,b,d)),"normal"===e&&b in Db&&(e=Db[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?zb.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Cb,function(){return Ib(a,b,d)}):Ib(a,b,d):void 0},set:function(a,c,d){var e=d&&wb(a);return Gb(a,c,d?Hb(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=yb(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xb,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ub.test(a)||(n.cssHooks[a+b].set=Gb)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wb(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Jb(this,!0)},hide:function(){return Jb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Kb(a,b,c,d,e){return new Kb.prototype.init(a,b,c,d,e)}n.Tween=Kb,Kb.prototype={constructor:Kb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Kb.propHooks[this.prop];return a&&a.get?a.get(this):Kb.propHooks._default.get(this)},run:function(a){var b,c=Kb.propHooks[this.prop];return this.pos=b=this.options.duration?n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Kb.propHooks._default.set(this),this}},Kb.prototype.init.prototype=Kb.prototype,Kb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Kb.propHooks.scrollTop=Kb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Kb.prototype.init,n.fx.step={};var Lb,Mb,Nb=/^(?:toggle|show|hide)$/,Ob=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pb=/queueHooks$/,Qb=[Vb],Rb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Ob.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Ob.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sb(){return setTimeout(function(){Lb=void 0}),Lb=n.now()}function Tb(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ub(a,b,c){for(var d,e=(Rb[b]||[]).concat(Rb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Vb(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||tb(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Nb.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?tb(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ub(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wb(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xb(a,b,c){var d,e,f=0,g=Qb.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Lb||Sb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:Lb||Sb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wb(k,j.opts.specialEasing);g>f;f++)if(d=Qb[f].call(j,a,k,j.opts))return d;return n.map(k,Ub,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xb,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Rb[c]=Rb[c]||[],Rb[c].unshift(b)},prefilter:function(a,b){b?Qb.unshift(a):Qb.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xb(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Tb(b,!0),a,d,e)}}),n.each({slideDown:Tb("show"),slideUp:Tb("hide"),slideToggle:Tb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(Lb=n.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||n.fx.stop(),Lb=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){Mb||(Mb=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(Mb),Mb=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a=l.createElement("input"),b=l.createElement("select"),c=b.appendChild(l.createElement("option"));a.type="checkbox",k.checkOn=""!==a.value,k.optSelected=c.selected,b.disabled=!0,k.optDisabled=!c.disabled,a=l.createElement("input"),a.value="t",a.type="radio",k.radioValue="t"===a.value}();var Yb,Zb,$b=n.expr.attrHandle;n.fn.extend({attr:function(a,b){return J(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Zb:Yb)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))
+},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Zb={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$b[b]||n.find.attr;$b[b]=function(a,b,d){var e,f;return d||(f=$b[b],$b[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$b[b]=f),e}});var _b=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_b.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ac=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ac," ").indexOf(b)>=0)return!0;return!1}});var bc=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bc,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cc=n.now(),dc=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var ec=/#.*$/,fc=/([?&])_=[^&]*/,gc=/^(.*?):[ \t]*([^\r\n]*)$/gm,hc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,ic=/^(?:GET|HEAD)$/,jc=/^\/\//,kc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,lc={},mc={},nc="*/".concat("*"),oc=a.location.href,pc=kc.exec(oc.toLowerCase())||[];function qc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function rc(a,b,c,d){var e={},f=a===mc;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function sc(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function tc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function uc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:oc,type:"GET",isLocal:hc.test(pc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":nc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?sc(sc(a,n.ajaxSettings),b):sc(n.ajaxSettings,a)},ajaxPrefilter:qc(lc),ajaxTransport:qc(mc),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=gc.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||oc)+"").replace(ec,"").replace(jc,pc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=kc.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===pc[1]&&h[2]===pc[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(pc[3]||("http:"===pc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),rc(lc,k,b,v),2===t)return v;i=n.event&&k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!ic.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(dc.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=fc.test(d)?d.replace(fc,"$1_="+cc++):d+(dc.test(d)?"&":"?")+"_="+cc++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+nc+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=rc(mc,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=tc(k,v,f)),u=uc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var vc=/%20/g,wc=/\[\]$/,xc=/\r?\n/g,yc=/^(?:submit|button|image|reset|file)$/i,zc=/^(?:input|select|textarea|keygen)/i;function Ac(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||wc.test(a)?d(a,e):Ac(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Ac(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Ac(c,a[c],b,e);return d.join("&").replace(vc,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&zc.test(this.nodeName)&&!yc.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(xc,"\r\n")}}):{name:b.name,value:c.replace(xc,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Bc=0,Cc={},Dc={0:200,1223:204},Ec=n.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in Cc)Cc[a]()}),k.cors=!!Ec&&"withCredentials"in Ec,k.ajax=Ec=!!Ec,n.ajaxTransport(function(a){var b;return k.cors||Ec&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Bc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Cc[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Dc[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Cc[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("<script>").prop({async:!0,charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&e("error"===a.type?404:200,a.type)}),l.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Fc=[],Gc=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Fc.pop()||n.expando+"_"+cc++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Gc.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Gc.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Gc,"$1"+e):b.jsonp!==!1&&(b.url+=(dc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Fc.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||l;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var Hc=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&Hc)return Hc.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=n.trim(a.slice(h)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e,dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,f||[a.responseText,b,a])}),this},n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var Ic=a.document.documentElement;function Jc(a){return n.isWindow(a)?a:9===a.nodeType&&a.defaultView}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d=this[0],e={top:0,left:0},f=d&&d.ownerDocument;if(f)return b=f.documentElement,n.contains(b,d)?(typeof d.getBoundingClientRect!==U&&(e=d.getBoundingClientRect()),c=Jc(f),{top:e.top+c.pageYOffset-b.clientTop,left:e.left+c.pageXOffset-b.clientLeft}):e},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===n.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(d=a.offset()),d.top+=n.css(a[0],"borderTopWidth",!0),d.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-d.top-n.css(c,"marginTop",!0),left:b.left-d.left-n.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||Ic;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Ic})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b,c){var d="pageYOffset"===c;n.fn[b]=function(e){return J(this,function(b,e,f){var g=Jc(b);return void 0===f?g?g[c]:b[e]:void(g?g.scrollTo(d?a.pageXOffset:f,d?f:a.pageYOffset):b[e]=f)},b,e,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=yb(k.pixelPosition,function(a,c){return c?(c=xb(a,b),vb.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return J(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var Kc=a.jQuery,Lc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=Lc),b&&a.jQuery===n&&(a.jQuery=Kc),n},typeof b===U&&(a.jQuery=a.$=n),n});
diff --git a/test/unit/modules/compiler/codeframe.spec.ts b/test/unit/modules/compiler/codeframe.spec.ts
deleted file mode 100644
index 4ed904d78bf..00000000000
--- a/test/unit/modules/compiler/codeframe.spec.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { generateCodeFrame } from 'compiler/codeframe'
-
-describe('codeframe', () => {
-  const source = `
-<div>
-  <template key="one"></template>
-  <ul>
-    <li v-for="foobar">hi</li>
-  </ul>
-  <template key="two"></template>
-</div>
-    `.trim()
-
-  it('line near top', () => {
-    const keyStart = source.indexOf(`key="one"`)
-    const keyEnd = keyStart + `key="one"`.length
-    expect(generateCodeFrame(source, keyStart, keyEnd)).toBe(
-      `
-1  |  <div>
-2  |    <template key="one"></template>
-   |              ^^^^^^^^^
-3  |    <ul>
-4  |      <li v-for="foobar">hi</li>
-    `.trim()
-    )
-  })
-
-  it('line in middle', () => {
-    // should cover 5 lines
-    const forStart = source.indexOf(`v-for=`)
-    const forEnd = forStart + `v-for="foobar"`.length
-    expect(generateCodeFrame(source, forStart, forEnd)).toBe(
-      `
-2  |    <template key="one"></template>
-3  |    <ul>
-4  |      <li v-for="foobar">hi</li>
-   |          ^^^^^^^^^^^^^^
-5  |    </ul>
-6  |    <template key="two"></template>
-    `.trim()
-    )
-  })
-
-  it('line near bottom', () => {
-    const keyStart = source.indexOf(`key="two"`)
-    const keyEnd = keyStart + `key="two"`.length
-    expect(generateCodeFrame(source, keyStart, keyEnd)).toBe(
-      `
-4  |      <li v-for="foobar">hi</li>
-5  |    </ul>
-6  |    <template key="two"></template>
-   |              ^^^^^^^^^
-7  |  </div>
-    `.trim()
-    )
-  })
-
-  it('multi-line highlights', () => {
-    const source = `
-<div attr="some
-  multiline
-attr
-">
-</div>
-    `.trim()
-
-    const attrStart = source.indexOf(`attr=`)
-    const attrEnd = source.indexOf(`">`) + 1
-    expect(generateCodeFrame(source, attrStart, attrEnd)).toBe(
-      `
-1  |  <div attr="some
-   |       ^^^^^^^^^^
-2  |    multiline
-   |  ^^^^^^^^^^^
-3  |  attr
-   |  ^^^^
-4  |  ">
-   |  ^
-    `.trim()
-    )
-  })
-})
diff --git a/test/unit/modules/compiler/codegen.spec.ts b/test/unit/modules/compiler/codegen.spec.ts
deleted file mode 100644
index aab2505f254..00000000000
--- a/test/unit/modules/compiler/codegen.spec.ts
+++ /dev/null
@@ -1,743 +0,0 @@
-import { parse } from 'compiler/parser/index'
-import { optimize } from 'compiler/optimizer'
-import { generate } from 'compiler/codegen'
-import { isObject, isFunction, extend } from 'shared/util'
-import { isReservedTag } from 'web/util/index'
-import { baseOptions } from 'web/compiler/options'
-import { BindingTypes } from '../../../../packages/compiler-sfc/src/types'
-
-function assertCodegen(template, generatedCode, ...args) {
-  let staticRenderFnCodes: string[] = []
-  let generateOptions = baseOptions
-  let proc: Function | null = null
-  let len = args.length
-  while (len--) {
-    const arg = args[len]
-    if (Array.isArray(arg)) {
-      staticRenderFnCodes = arg
-    } else if (isObject(arg)) {
-      generateOptions = arg
-    } else if (isFunction(arg)) {
-      proc = arg
-    }
-  }
-  const ast = parse(template, baseOptions)
-  optimize(ast, baseOptions)
-  proc && proc(ast)
-  const res = generate(ast, generateOptions)
-  expect(res.render).toBe(generatedCode)
-  expect(res.staticRenderFns).toEqual(staticRenderFnCodes)
-}
-
-describe('codegen', () => {
-  it('generate directive', () => {
-    assertCodegen(
-      '<p v-custom1:arg1.modifier="value1" v-custom2></p>',
-      `with(this){return _c('p',{directives:[{name:"custom1",rawName:"v-custom1:arg1.modifier",value:(value1),expression:"value1",arg:"arg1",modifiers:{"modifier":true}},{name:"custom2",rawName:"v-custom2"}]})}`
-    )
-  })
-
-  it('generate filters', () => {
-    assertCodegen(
-      '<div :id="a | b | c">{{ d | e | f }}</div>',
-      `with(this){return _c('div',{attrs:{"id":_f("c")(_f("b")(a))}},[_v(_s(_f("f")(_f("e")(d))))])}`
-    )
-  })
-
-  it('generate filters with no arguments', () => {
-    assertCodegen(
-      '<div>{{ d | e() }}</div>',
-      `with(this){return _c('div',[_v(_s(_f("e")(d)))])}`
-    )
-  })
-
-  it('generate v-for directive', () => {
-    assertCodegen(
-      '<div><li v-for="item in items" :key="item.uid"></li></div>',
-      `with(this){return _c('div',_l((items),function(item){return _c('li',{key:item.uid})}),0)}`
-    )
-    // iterator syntax
-    assertCodegen(
-      '<div><li v-for="(item, i) in items"></li></div>',
-      `with(this){return _c('div',_l((items),function(item,i){return _c('li')}),0)}`
-    )
-    assertCodegen(
-      '<div><li v-for="(item, key, index) in items"></li></div>',
-      `with(this){return _c('div',_l((items),function(item,key,index){return _c('li')}),0)}`
-    )
-    // destructuring
-    assertCodegen(
-      '<div><li v-for="{ a, b } in items"></li></div>',
-      `with(this){return _c('div',_l((items),function({ a, b }){return _c('li')}),0)}`
-    )
-    assertCodegen(
-      '<div><li v-for="({ a, b }, key, index) in items"></li></div>',
-      `with(this){return _c('div',_l((items),function({ a, b },key,index){return _c('li')}),0)}`
-    )
-    // v-for with extra element
-    assertCodegen(
-      '<div><p></p><li v-for="item in items"></li></div>',
-      `with(this){return _c('div',[_c('p'),_l((items),function(item){return _c('li')})],2)}`
-    )
-  })
-
-  it('generate v-if directive', () => {
-    assertCodegen(
-      '<p v-if="show">hello</p>',
-      `with(this){return (show)?_c('p',[_v("hello")]):_e()}`
-    )
-  })
-
-  it('generate v-else directive', () => {
-    assertCodegen(
-      '<div><p v-if="show">hello</p><p v-else>world</p></div>',
-      `with(this){return _c('div',[(show)?_c('p',[_v("hello")]):_c('p',[_v("world")])])}`
-    )
-  })
-
-  it('generate v-else-if directive', () => {
-    assertCodegen(
-      '<div><p v-if="show">hello</p><p v-else-if="hide">world</p></div>',
-      `with(this){return _c('div',[(show)?_c('p',[_v("hello")]):(hide)?_c('p',[_v("world")]):_e()])}`
-    )
-  })
-
-  it('generate v-else-if with v-else directive', () => {
-    assertCodegen(
-      '<div><p v-if="show">hello</p><p v-else-if="hide">world</p><p v-else>bye</p></div>',
-      `with(this){return _c('div',[(show)?_c('p',[_v("hello")]):(hide)?_c('p',[_v("world")]):_c('p',[_v("bye")])])}`
-    )
-  })
-
-  it('generate multi v-else-if with v-else directive', () => {
-    assertCodegen(
-      '<div><p v-if="show">hello</p><p v-else-if="hide">world</p><p v-else-if="3">elseif</p><p v-else>bye</p></div>',
-      `with(this){return _c('div',[(show)?_c('p',[_v("hello")]):(hide)?_c('p',[_v("world")]):(3)?_c('p',[_v("elseif")]):_c('p',[_v("bye")])])}`
-    )
-  })
-
-  it('generate ref', () => {
-    assertCodegen(
-      '<p ref="component1"></p>',
-      `with(this){return _c('p',{ref:"component1"})}`
-    )
-  })
-
-  it('generate ref on v-for', () => {
-    assertCodegen(
-      '<ul><li v-for="item in items" ref="component1"></li></ul>',
-      `with(this){return _c('ul',_l((items),function(item){return _c('li',{ref:"component1",refInFor:true})}),0)}`
-    )
-  })
-
-  it('generate v-bind directive', () => {
-    assertCodegen(
-      '<p v-bind="test"></p>',
-      `with(this){return _c('p',_b({},'p',test,false))}`
-    )
-  })
-
-  it('generate v-bind with prop directive', () => {
-    assertCodegen(
-      '<p v-bind.prop="test"></p>',
-      `with(this){return _c('p',_b({},'p',test,true))}`
-    )
-  })
-
-  it('generate v-bind directive with sync modifier', () => {
-    assertCodegen(
-      '<p v-bind.sync="test"></p>',
-      `with(this){return _c('p',_b({},'p',test,false,true))}`
-    )
-  })
-
-  it('generate v-model directive', () => {
-    assertCodegen(
-      '<input v-model="test">',
-      `with(this){return _c('input',{directives:[{name:"model",rawName:"v-model",value:(test),expression:"test"}],domProps:{"value":(test)},on:{"input":function($event){if($event.target.composing)return;test=$event.target.value}}})}`
-    )
-  })
-
-  it('generate multiline v-model directive', () => {
-    assertCodegen(
-      '<input v-model="\n test \n">',
-      `with(this){return _c('input',{directives:[{name:"model",rawName:"v-model",value:(\n test \n),expression:"\\n test \\n"}],domProps:{"value":(\n test \n)},on:{"input":function($event){if($event.target.composing)return;\n test \n=$event.target.value}}})}`
-    )
-  })
-
-  it('generate multiline v-model directive on custom component', () => {
-    assertCodegen(
-      '<my-component v-model="\n test \n" />',
-      `with(this){return _c('my-component',{model:{value:(\n test \n),callback:function ($$v) {\n test \n=$$v},expression:"\\n test \\n"}})}`
-    )
-  })
-
-  it('generate template tag', () => {
-    assertCodegen(
-      '<div><template><p>{{hello}}</p></template></div>',
-      `with(this){return _c('div',[[_c('p',[_v(_s(hello))])]],2)}`
-    )
-  })
-
-  it('generate single slot', () => {
-    assertCodegen(
-      '<div><slot></slot></div>',
-      `with(this){return _c('div',[_t("default")],2)}`
-    )
-  })
-
-  it('generate named slot', () => {
-    assertCodegen(
-      '<div><slot name="one"></slot></div>',
-      `with(this){return _c('div',[_t("one")],2)}`
-    )
-  })
-
-  it('generate slot fallback content', () => {
-    assertCodegen(
-      '<div><slot><div>hi</div></slot></div>',
-      `with(this){return _c('div',[_t("default",function(){return [_c('div',[_v("hi")])]})],2)}`
-    )
-  })
-
-  it('generate slot target', () => {
-    assertCodegen(
-      '<p slot="one">hello world</p>',
-      `with(this){return _c('p',{attrs:{"slot":"one"},slot:"one"},[_v("hello world")])}`
-    )
-  })
-
-  it('generate scoped slot', () => {
-    assertCodegen(
-      '<foo><template slot-scope="bar">{{ bar }}</template></foo>',
-      `with(this){return _c('foo',{scopedSlots:_u([{key:"default",fn:function(bar){return [_v(_s(bar))]}}])})}`
-    )
-    assertCodegen(
-      '<foo><div slot-scope="bar">{{ bar }}</div></foo>',
-      `with(this){return _c('foo',{scopedSlots:_u([{key:"default",fn:function(bar){return _c('div',{},[_v(_s(bar))])}}])})}`
-    )
-  })
-
-  it('generate named scoped slot', () => {
-    assertCodegen(
-      '<foo><template slot="foo" slot-scope="bar">{{ bar }}</template></foo>',
-      `with(this){return _c('foo',{scopedSlots:_u([{key:"foo",fn:function(bar){return [_v(_s(bar))]}}])})}`
-    )
-    assertCodegen(
-      '<foo><div slot="foo" slot-scope="bar">{{ bar }}</div></foo>',
-      `with(this){return _c('foo',{scopedSlots:_u([{key:"foo",fn:function(bar){return _c('div',{},[_v(_s(bar))])}}])})}`
-    )
-  })
-
-  it('generate dynamic scoped slot', () => {
-    assertCodegen(
-      '<foo><template :slot="foo" slot-scope="bar">{{ bar }}</template></foo>',
-      `with(this){return _c('foo',{scopedSlots:_u([{key:foo,fn:function(bar){return [_v(_s(bar))]}}],null,true)})}`
-    )
-  })
-
-  it('generate scoped slot with multiline v-if', () => {
-    assertCodegen(
-      '<foo><template v-if="\nshow\n" slot-scope="bar">{{ bar }}</template></foo>',
-      `with(this){return _c('foo',{scopedSlots:_u([{key:"default",fn:function(bar){return (\nshow\n)?[_v(_s(bar))]:undefined}}],null,true)})}`
-    )
-    assertCodegen(
-      '<foo><div v-if="\nshow\n" slot="foo" slot-scope="bar">{{ bar }}</div></foo>',
-      `with(this){return _c(\'foo\',{scopedSlots:_u([{key:"foo",fn:function(bar){return (\nshow\n)?_c(\'div\',{},[_v(_s(bar))]):_e()}}],null,true)})}`
-    )
-  })
-
-  it('generate scoped slot with new slot syntax', () => {
-    assertCodegen(
-      '<foo><template v-if="show" #default="bar">{{ bar }}</template></foo>',
-      `with(this){return _c('foo',{scopedSlots:_u([(show)?{key:"default",fn:function(bar){return [_v(_s(bar))]}}:null],null,true)})}`
-    )
-  })
-
-  it('generate class binding', () => {
-    // static
-    assertCodegen(
-      '<p class="class1">hello world</p>',
-      `with(this){return _c('p',{staticClass:"class1"},[_v("hello world")])}`
-    )
-    // dynamic
-    assertCodegen(
-      '<p :class="class1">hello world</p>',
-      `with(this){return _c('p',{class:class1},[_v("hello world")])}`
-    )
-  })
-
-  it('generate style binding', () => {
-    assertCodegen(
-      '<p :style="error">hello world</p>',
-      `with(this){return _c('p',{style:(error)},[_v("hello world")])}`
-    )
-  })
-
-  it('generate v-show directive', () => {
-    assertCodegen(
-      '<p v-show="shown">hello world</p>',
-      `with(this){return _c('p',{directives:[{name:"show",rawName:"v-show",value:(shown),expression:"shown"}]},[_v("hello world")])}`
-    )
-  })
-
-  it('generate DOM props with v-bind directive', () => {
-    // input + value
-    assertCodegen(
-      '<input :value="msg">',
-      `with(this){return _c('input',{domProps:{"value":msg}})}`
-    )
-    // non input
-    assertCodegen(
-      '<p :value="msg"/>',
-      `with(this){return _c('p',{attrs:{"value":msg}})}`
-    )
-  })
-
-  it('generate attrs with v-bind directive', () => {
-    assertCodegen(
-      '<input :name="field1">',
-      `with(this){return _c('input',{attrs:{"name":field1}})}`
-    )
-  })
-
-  it('generate static attrs', () => {
-    assertCodegen(
-      '<input name="field1">',
-      `with(this){return _c('input',{attrs:{"name":"field1"}})}`
-    )
-  })
-
-  it('generate events with v-on directive', () => {
-    assertCodegen(
-      '<input @input="onInput">',
-      `with(this){return _c('input',{on:{"input":onInput}})}`
-    )
-  })
-
-  it('generate events with method call', () => {
-    assertCodegen(
-      '<input @input="onInput($event);">',
-      `with(this){return _c('input',{on:{"input":function($event){return onInput($event);}}})}`
-    )
-    // empty arguments
-    assertCodegen(
-      '<input @input="onInput();">',
-      `with(this){return _c('input',{on:{"input":function($event){return onInput();}}})}`
-    )
-    // without semicolon
-    assertCodegen(
-      '<input @input="onInput($event)">',
-      `with(this){return _c('input',{on:{"input":function($event){return onInput($event)}}})}`
-    )
-    // multiple args
-    assertCodegen(
-      '<input @input="onInput($event, \'abc\', 5);">',
-      `with(this){return _c('input',{on:{"input":function($event){return onInput($event, 'abc', 5);}}})}`
-    )
-    // expression in args
-    assertCodegen(
-      '<input @input="onInput($event, 2+2);">',
-      `with(this){return _c('input',{on:{"input":function($event){return onInput($event, 2+2);}}})}`
-    )
-    // tricky symbols in args
-    assertCodegen(
-      `<input @input="onInput(');[\\'());');">`,
-      `with(this){return _c('input',{on:{"input":function($event){onInput(');[\\'());');}}})}`
-    )
-    // function name including a `function` part (#9920)
-    assertCodegen(
-      '<input @input="functionName()">',
-      `with(this){return _c('input',{on:{"input":function($event){return functionName()}}})}`
-    )
-  })
-
-  it('generate events with multiple statements', () => {
-    // normal function
-    assertCodegen(
-      '<input @input="onInput1();onInput2()">',
-      `with(this){return _c('input',{on:{"input":function($event){onInput1();onInput2()}}})}`
-    )
-    // function with multiple args
-    assertCodegen(
-      "<input @input=\"onInput1($event, 'text');onInput2('text2', $event)\">",
-      `with(this){return _c('input',{on:{"input":function($event){onInput1($event, 'text');onInput2('text2', $event)}}})}`
-    )
-  })
-
-  it('generate events with keycode', () => {
-    assertCodegen(
-      '<input @input.enter="onInput">',
-      `with(this){return _c('input',{on:{"input":function($event){if(!$event.type.indexOf('key')&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;return onInput.apply(null, arguments)}}})}`
-    )
-    // multiple keycodes (delete)
-    assertCodegen(
-      '<input @input.delete="onInput">',
-      `with(this){return _c('input',{on:{"input":function($event){if(!$event.type.indexOf('key')&&_k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete","Del"]))return null;return onInput.apply(null, arguments)}}})}`
-    )
-    // multiple keycodes (esc)
-    assertCodegen(
-      '<input @input.esc="onInput">',
-      `with(this){return _c('input',{on:{"input":function($event){if(!$event.type.indexOf('key')&&_k($event.keyCode,"esc",27,$event.key,["Esc","Escape"]))return null;return onInput.apply(null, arguments)}}})}`
-    )
-    // multiple keycodes (space)
-    assertCodegen(
-      '<input @input.space="onInput">',
-      `with(this){return _c('input',{on:{"input":function($event){if(!$event.type.indexOf('key')&&_k($event.keyCode,"space",32,$event.key,[" ","Spacebar"]))return null;return onInput.apply(null, arguments)}}})}`
-    )
-    // multiple keycodes (chained)
-    assertCodegen(
-      '<input @keydown.enter.delete="onInput">',
-      `with(this){return _c('input',{on:{"keydown":function($event){if(!$event.type.indexOf('key')&&_k($event.keyCode,"enter",13,$event.key,"Enter")&&_k($event.keyCode,"delete",[8,46],$event.key,["Backspace","Delete","Del"]))return null;return onInput.apply(null, arguments)}}})}`
-    )
-    // number keycode
-    assertCodegen(
-      '<input @input.13="onInput">',
-      `with(this){return _c('input',{on:{"input":function($event){if(!$event.type.indexOf('key')&&$event.keyCode!==13)return null;return onInput.apply(null, arguments)}}})}`
-    )
-    // custom keycode
-    assertCodegen(
-      '<input @input.custom="onInput">',
-      `with(this){return _c('input',{on:{"input":function($event){if(!$event.type.indexOf('key')&&_k($event.keyCode,"custom",undefined,$event.key,undefined))return null;return onInput.apply(null, arguments)}}})}`
-    )
-  })
-
-  it('generate events with generic modifiers', () => {
-    assertCodegen(
-      '<input @input.stop="onInput">',
-      `with(this){return _c('input',{on:{"input":function($event){$event.stopPropagation();return onInput.apply(null, arguments)}}})}`
-    )
-    assertCodegen(
-      '<input @input.prevent="onInput">',
-      `with(this){return _c('input',{on:{"input":function($event){$event.preventDefault();return onInput.apply(null, arguments)}}})}`
-    )
-    assertCodegen(
-      '<input @input.self="onInput">',
-      `with(this){return _c('input',{on:{"input":function($event){if($event.target !== $event.currentTarget)return null;return onInput.apply(null, arguments)}}})}`
-    )
-  })
-
-  // GitHub Issues #5146
-  it('generate events with generic modifiers and keycode correct order', () => {
-    assertCodegen(
-      '<input @keydown.enter.prevent="onInput">',
-      `with(this){return _c('input',{on:{"keydown":function($event){if(!$event.type.indexOf('key')&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;$event.preventDefault();return onInput.apply(null, arguments)}}})}`
-    )
-
-    assertCodegen(
-      '<input @keydown.enter.stop="onInput">',
-      `with(this){return _c('input',{on:{"keydown":function($event){if(!$event.type.indexOf('key')&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;$event.stopPropagation();return onInput.apply(null, arguments)}}})}`
-    )
-  })
-
-  it('generate events with mouse event modifiers', () => {
-    assertCodegen(
-      '<input @click.ctrl="onClick">',
-      `with(this){return _c('input',{on:{"click":function($event){if(!$event.ctrlKey)return null;return onClick.apply(null, arguments)}}})}`
-    )
-    assertCodegen(
-      '<input @click.shift="onClick">',
-      `with(this){return _c('input',{on:{"click":function($event){if(!$event.shiftKey)return null;return onClick.apply(null, arguments)}}})}`
-    )
-    assertCodegen(
-      '<input @click.alt="onClick">',
-      `with(this){return _c('input',{on:{"click":function($event){if(!$event.altKey)return null;return onClick.apply(null, arguments)}}})}`
-    )
-    assertCodegen(
-      '<input @click.meta="onClick">',
-      `with(this){return _c('input',{on:{"click":function($event){if(!$event.metaKey)return null;return onClick.apply(null, arguments)}}})}`
-    )
-    assertCodegen(
-      '<input @click.exact="onClick">',
-      `with(this){return _c('input',{on:{"click":function($event){if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey)return null;return onClick.apply(null, arguments)}}})}`
-    )
-    assertCodegen(
-      '<input @click.ctrl.exact="onClick">',
-      `with(this){return _c('input',{on:{"click":function($event){if(!$event.ctrlKey)return null;if($event.shiftKey||$event.altKey||$event.metaKey)return null;return onClick.apply(null, arguments)}}})}`
-    )
-  })
-
-  it('generate events with multiple modifiers', () => {
-    assertCodegen(
-      '<input @input.stop.prevent.self="onInput">',
-      `with(this){return _c('input',{on:{"input":function($event){$event.stopPropagation();$event.preventDefault();if($event.target !== $event.currentTarget)return null;return onInput.apply(null, arguments)}}})}`
-    )
-  })
-
-  it('generate events with capture modifier', () => {
-    assertCodegen(
-      '<input @input.capture="onInput">',
-      `with(this){return _c('input',{on:{"!input":function($event){return onInput.apply(null, arguments)}}})}`
-    )
-  })
-
-  it('generate events with once modifier', () => {
-    assertCodegen(
-      '<input @input.once="onInput">',
-      `with(this){return _c('input',{on:{"~input":function($event){return onInput.apply(null, arguments)}}})}`
-    )
-  })
-
-  it('generate events with capture and once modifier', () => {
-    assertCodegen(
-      '<input @input.capture.once="onInput">',
-      `with(this){return _c('input',{on:{"~!input":function($event){return onInput.apply(null, arguments)}}})}`
-    )
-  })
-
-  it('generate events with once and capture modifier', () => {
-    assertCodegen(
-      '<input @input.once.capture="onInput">',
-      `with(this){return _c('input',{on:{"~!input":function($event){return onInput.apply(null, arguments)}}})}`
-    )
-  })
-
-  it('generate events with inline statement', () => {
-    assertCodegen(
-      '<input @input="current++">',
-      `with(this){return _c('input',{on:{"input":function($event){current++}}})}`
-    )
-  })
-
-  it('generate events with inline function expression', () => {
-    // normal function
-    assertCodegen(
-      '<input @input="function () { current++ }">',
-      `with(this){return _c('input',{on:{"input":function () { current++ }}})}`
-    )
-    // normal named function
-    assertCodegen(
-      '<input @input="function fn () { current++ }">',
-      `with(this){return _c('input',{on:{"input":function fn () { current++ }}})}`
-    )
-    // arrow with no args
-    assertCodegen(
-      '<input @input="()=>current++">',
-      `with(this){return _c('input',{on:{"input":()=>current++}})}`
-    )
-    // arrow with parens, single arg
-    assertCodegen(
-      '<input @input="(e) => current++">',
-      `with(this){return _c('input',{on:{"input":(e) => current++}})}`
-    )
-    // arrow with parens, multi args
-    assertCodegen(
-      '<input @input="(a, b, c) => current++">',
-      `with(this){return _c('input',{on:{"input":(a, b, c) => current++}})}`
-    )
-    // arrow with destructuring
-    assertCodegen(
-      '<input @input="({ a, b }) => current++">',
-      `with(this){return _c('input',{on:{"input":({ a, b }) => current++}})}`
-    )
-    // arrow single arg no parens
-    assertCodegen(
-      '<input @input="e=>current++">',
-      `with(this){return _c('input',{on:{"input":e=>current++}})}`
-    )
-    // with modifiers
-    assertCodegen(
-      `<input @keyup.enter="e=>current++">`,
-      `with(this){return _c('input',{on:{"keyup":function($event){if(!$event.type.indexOf('key')&&_k($event.keyCode,"enter",13,$event.key,"Enter"))return null;return (e=>current++).apply(null, arguments)}}})}`
-    )
-  })
-
-  // #3893
-  it('should not treat handler with unexpected whitespace as inline statement', () => {
-    assertCodegen(
-      '<input @input=" onInput ">',
-      `with(this){return _c('input',{on:{"input":onInput}})}`
-    )
-  })
-
-  it('generate unhandled events', () => {
-    assertCodegen(
-      '<input @input="current++">',
-      `with(this){return _c('input',{on:{"input":function(){}}})}`,
-      ast => {
-        ast.events.input = undefined
-      }
-    )
-  })
-
-  it('generate multiple event handlers', () => {
-    assertCodegen(
-      '<input @input="current++" @input.stop="onInput">',
-      `with(this){return _c('input',{on:{"input":[function($event){current++},function($event){$event.stopPropagation();return onInput.apply(null, arguments)}]}})}`
-    )
-  })
-
-  it('generate component', () => {
-    assertCodegen(
-      '<my-component name="mycomponent1" :msg="msg" @notify="onNotify"><div>hi</div></my-component>',
-      `with(this){return _c('my-component',{attrs:{"name":"mycomponent1","msg":msg},on:{"notify":onNotify}},[_c('div',[_v("hi")])])}`
-    )
-  })
-
-  it('generate svg component with children', () => {
-    assertCodegen(
-      '<svg><my-comp><circle :r="10"></circle></my-comp></svg>',
-      `with(this){return _c('svg',[_c('my-comp',[_c('circle',{attrs:{"r":10}})])],1)}`
-    )
-  })
-
-  it('generate is attribute', () => {
-    assertCodegen(
-      '<div is="component1"></div>',
-      `with(this){return _c("component1",{tag:"div"})}`
-    )
-    assertCodegen(
-      '<div :is="component1"></div>',
-      `with(this){return _c(component1,{tag:"div"})}`
-    )
-    // maybe a component and normalize type should be 1
-    assertCodegen(
-      '<div><div is="component1"></div></div>',
-      `with(this){return _c('div',[_c("component1",{tag:"div"})],1)}`
-    )
-  })
-
-  it('generate component with inline-template', () => {
-    // have "inline-template'"
-    assertCodegen(
-      '<my-component inline-template><p><span>hello world</span></p></my-component>',
-      `with(this){return _c('my-component',{inlineTemplate:{render:function(){with(this){return _m(0)}},staticRenderFns:[function(){with(this){return _c('p',[_c('span',[_v("hello world")])])}}]}})}`
-    )
-    // "have inline-template attrs, but not having exactly one child element
-    assertCodegen(
-      '<my-component inline-template><hr><hr></my-component>',
-      `with(this){return _c('my-component',{inlineTemplate:{render:function(){with(this){return _c('hr')}},staticRenderFns:[]}})}`
-    )
-    assertCodegen(
-      '<my-component inline-template></my-component>',
-      `with(this){return _c('my-component',{})}`
-    )
-    // have "is" attribute
-    assertCodegen(
-      '<div is="myComponent" inline-template><div></div></div>',
-      `with(this){return _c("myComponent",{tag:"div",inlineTemplate:{render:function(){with(this){return _c('div')}},staticRenderFns:[]}})}`
-    )
-    assertCodegen(
-      '<div is="myComponent" inline-template></div>',
-      `with(this){return _c("myComponent",{tag:"div"})}`
-    )
-    expect(
-      'Inline-template components must have exactly one child element.'
-    ).toHaveBeenWarned()
-    expect((console.error as any).mock.calls.length).toBe(3)
-  })
-
-  it('generate static trees inside v-for', () => {
-    assertCodegen(
-      `<div><div v-for="i in 10"><p><span></span></p></div></div>`,
-      `with(this){return _c('div',_l((10),function(i){return _c('div',[_m(0,true)])}),0)}`,
-      [`with(this){return _c('p',[_c('span')])}`]
-    )
-  })
-
-  it('generate component with v-for', () => {
-    // normalize type: 2
-    assertCodegen(
-      '<div><child></child><template v-for="item in list">{{ item }}</template></div>',
-      `with(this){return _c('div',[_c('child'),_l((list),function(item){return [_v(_s(item))]})],2)}`
-    )
-  })
-
-  it('generate component with comment', () => {
-    const options = extend(
-      {
-        comments: true
-      },
-      baseOptions
-    )
-    const template = '<div><!--comment--></div>'
-    const generatedCode = `with(this){return _c('div',[_e("comment")])}`
-
-    const ast = parse(template, options)
-    optimize(ast, options)
-    const res = generate(ast, options)
-    expect(res.render).toBe(generatedCode)
-  })
-
-  // #6150
-  it('generate comments with special characters', () => {
-    const options = extend(
-      {
-        comments: true
-      },
-      baseOptions
-    )
-    const template = "<div><!--\n'comment'\n--></div>"
-    const generatedCode = `with(this){return _c('div',[_e("\\n'comment'\\n")])}`
-
-    const ast = parse(template, options)
-    optimize(ast, options)
-    const res = generate(ast, options)
-    expect(res.render).toBe(generatedCode)
-  })
-
-  // #8041
-  it('does not squash templates inside v-pre', () => {
-    const template = '<div v-pre><template><p>{{msg}}</p></template></div>'
-    const generatedCode = `with(this){return _m(0)}`
-    const renderFn = `with(this){return _c('div',{pre:true},[_c('template',[_c('p',[_v("{{msg}}")])])],2)}`
-    const ast = parse(template, baseOptions)
-    optimize(ast, baseOptions)
-    const res = generate(ast, baseOptions)
-    expect(res.render).toBe(generatedCode)
-    expect(res.staticRenderFns).toEqual([renderFn])
-  })
-
-  it('not specified ast type', () => {
-    const res = generate(undefined, baseOptions)
-    expect(res.render).toBe(`with(this){return _c("div")}`)
-    expect(res.staticRenderFns).toEqual([])
-  })
-
-  it('not specified directives option', () => {
-    assertCodegen(
-      '<p v-if="show">hello world</p>',
-      `with(this){return (show)?_c('p',[_v("hello world")]):_e()}`,
-      { isReservedTag }
-    )
-  })
-
-  // #9142
-  it('should compile single v-for component inside template', () => {
-    assertCodegen(
-      `<div><template v-if="ok"><foo v-for="i in 1" :key="i"></foo></template></div>`,
-      `with(this){return _c('div',[(ok)?_l((1),function(i){return _c('foo',{key:i})}):_e()],2)}`
-    )
-  })
-
-  it('component with bindings ', () => {
-    const ast = parse(`<div><Foo/><foo-bar></foo-bar></div>`, baseOptions)
-    optimize(ast, baseOptions)
-    const res = generate(ast, {
-      ...baseOptions,
-      bindings: {
-        Foo: BindingTypes.SETUP_CONST,
-        FooBar: BindingTypes.SETUP_CONST
-      }
-    })
-    expect(res.render).toMatchInlineSnapshot(
-      '"with(this){return _c(\'div\',[_c(Foo),_c(FooBar)],1)}"'
-    )
-  })
-
-  // #12674
-  it('component with bindings: should not resolve native elements', () => {
-    const ast = parse(`<div><form>{{ n }}</form></div>`, baseOptions)
-    optimize(ast, baseOptions)
-    const res = generate(ast, {
-      ...baseOptions,
-      bindings: {
-        form: BindingTypes.SETUP_CONST
-      }
-    })
-    expect(res.render).toMatch(`_c('form'`)
-    expect(res.render).toMatchInlineSnapshot(
-      "\"with(this){return _c('div',[_c('form',[_v(_s(n))])])}\""
-    )
-  })
-})
diff --git a/test/unit/modules/compiler/compiler-options.spec.ts b/test/unit/modules/compiler/compiler-options.spec.ts
deleted file mode 100644
index 865ad0b30c9..00000000000
--- a/test/unit/modules/compiler/compiler-options.spec.ts
+++ /dev/null
@@ -1,170 +0,0 @@
-import Vue from 'vue'
-import { compile } from 'web/compiler'
-import { getAndRemoveAttr } from 'compiler/helpers'
-
-describe('compile options', () => {
-  it('should be compiled', () => {
-    const { render, staticRenderFns, errors } = compile(
-      `
-      <div>
-        <input type="text" v-model="msg" required max="8" v-validate:field1.group1.group2>
-      </div>
-    `,
-      {
-        directives: {
-          validate(el, dir) {
-            if (dir.name === 'validate' && dir.arg) {
-              el.validate = {
-                field: dir.arg,
-                groups: dir.modifiers ? Object.keys(dir.modifiers) : []
-              }
-            }
-          }
-        },
-        modules: [
-          {
-            transformNode(el) {
-              el.validators = el.validators || []
-              const validators = [
-                'required',
-                'min',
-                'max',
-                'pattern',
-                'maxlength',
-                'minlength'
-              ]
-              validators.forEach(name => {
-                const rule = getAndRemoveAttr(el, name)
-                if (rule !== undefined) {
-                  el.validators.push({ name, rule })
-                }
-              })
-            },
-            genData(el) {
-              let data = ''
-              if (el.validate) {
-                data += `validate:${JSON.stringify(el.validate)},`
-              }
-              if (el.validators) {
-                data += `validators:${JSON.stringify(el.validators)},`
-              }
-              return data
-            },
-            transformCode(el, code) {
-              // check
-              if (!el.validate || !el.validators) {
-                return code
-              }
-              // setup validation result props
-              const result = { dirty: false } // define something other prop
-              el.validators.forEach(validator => {
-                result[validator.name] = null
-              })
-              // generate code
-              return `_c('validate',{props:{
-              field:${JSON.stringify(el.validate.field)},
-              groups:${JSON.stringify(el.validate.groups)},
-              validators:${JSON.stringify(el.validators)},
-              result:${JSON.stringify(result)},
-              child:${code}}
-            })`
-            }
-          }
-        ]
-      }
-    )
-    expect(render).not.toBeUndefined()
-    expect(staticRenderFns).toEqual([])
-    expect(errors).toEqual([])
-
-    const renderFn = new Function(render)
-    const vm = new Vue({
-      data: {
-        msg: 'hello'
-      },
-      components: {
-        validate: {
-          props: ['field', 'groups', 'validators', 'result', 'child'],
-          render(h) {
-            return this.child
-          },
-          computed: {
-            valid() {
-              let ret = true
-              for (let i = 0; i < this.validators.length; i++) {
-                const { name } = this.validators[i]
-                if (!this.result[name]) {
-                  ret = false
-                  break
-                }
-              }
-              return ret
-            }
-          },
-          mounted() {
-            // initialize validation
-            const value = this.$el.value
-            this.validators.forEach(validator => {
-              const ret = this[validator.name](value, validator.rule)
-              this.result[validator.name] = ret
-            })
-          },
-          methods: {
-            // something validators logic
-            required(val) {
-              return val.length > 0
-            },
-            max(val, rule) {
-              return !(parseInt(val, 10) > parseInt(rule, 10))
-            }
-          }
-        }
-      },
-      render: renderFn,
-      staticRenderFns
-    }).$mount()
-    expect(vm.$el.innerHTML).toBe('<input type="text">')
-    expect(vm.$children[0].valid).toBe(true)
-  })
-
-  it('should collect errors', () => {
-    let compiled = compile('hello')
-    expect(compiled.errors.length).toBe(1)
-    expect(compiled.errors[0]).toContain('root element')
-
-    compiled = compile('<div v-if="a----">{{ b++++ }}</div>')
-    expect(compiled.errors.length).toBe(2)
-    expect(compiled.errors[0]).toContain('Raw expression: v-if="a----"')
-    expect(compiled.errors[1]).toContain('Raw expression: {{ b++++ }}')
-  })
-
-  it('should collect errors with source range', () => {
-    let compiled = compile('hello', { outputSourceRange: true })
-    expect(compiled.errors.length).toBe(1)
-    expect(compiled.errors[0].start).toBe(0)
-    expect(compiled.errors[0].end).toBeUndefined()
-
-    compiled = compile('<div v-if="a----">{{ b++++ }}</div>', {
-      outputSourceRange: true
-    })
-    expect(compiled.errors.length).toBe(2)
-    expect(compiled.errors[0].start).toBe(5)
-    expect(compiled.errors[0].end).toBe(17)
-    expect(compiled.errors[1].start).toBe(18)
-    expect(compiled.errors[1].end).toBe(29)
-
-    compiled = compile('<div><span></div>', { outputSourceRange: true })
-    expect(compiled.errors.length).toBe(1)
-    expect(compiled.errors[0].start).toBe(5)
-    expect(compiled.errors[0].end).toBe(11)
-  })
-
-  it('should collect source range for binding keys', () => {
-    const compiled = compile('<div><slot v-bind:key="key" /></div>', {
-      outputSourceRange: true
-    })
-    expect(compiled.errors.length).toBe(1)
-    expect(compiled.errors[0].start).toBe(11)
-    expect(compiled.errors[0].end).toBe(27)
-  })
-})
diff --git a/test/unit/modules/compiler/optimizer.spec.ts b/test/unit/modules/compiler/optimizer.spec.ts
deleted file mode 100644
index a2b9e351ae3..00000000000
--- a/test/unit/modules/compiler/optimizer.spec.ts
+++ /dev/null
@@ -1,323 +0,0 @@
-import { parse } from 'compiler/parser/index'
-import { extend } from 'shared/util'
-import { optimize } from 'compiler/optimizer'
-import { baseOptions } from 'web/compiler/options'
-
-describe('optimizer', () => {
-  it('simple', () => {
-    const ast = parse(
-      '<h1 id="section1"><span>hello world</span></h1>',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(true) // h1
-    expect(ast.staticRoot).toBe(true)
-    expect(ast.children[0].static).toBe(true) // span
-  })
-
-  it('simple with comment', () => {
-    const options = extend(
-      {
-        comments: true
-      },
-      baseOptions
-    )
-    const ast = parse(
-      '<h1 id="section1"><span>hello world</span><!--comment--></h1>',
-      options
-    )
-    optimize(ast, options)
-    expect(ast.static).toBe(true) // h1
-    expect(ast.staticRoot).toBe(true)
-    expect(ast.children.length).toBe(2)
-    expect(ast.children[0].static).toBe(true) // span
-    expect(ast.children[1].static).toBe(true) // comment
-  })
-
-  it('skip simple nodes', () => {
-    const ast = parse('<h1 id="section1">hello</h1>', baseOptions)
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(true)
-    expect(ast.staticRoot).toBe(false) // this is too simple to warrant a static tree
-  })
-
-  it('interpolation', () => {
-    const ast = parse('<h1>{{msg}}</h1>', baseOptions)
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false) // h1
-    expect(ast.children[0].static).toBe(false) // text node with interpolation
-  })
-
-  it('nested elements', () => {
-    const ast = parse('<ul><li>hello</li><li>world</li></ul>', baseOptions)
-    optimize(ast, baseOptions)
-    // ul
-    expect(ast.static).toBe(true)
-    expect(ast.staticRoot).toBe(true)
-    // li
-    expect(ast.children[0].static).toBe(true) // first
-    expect(ast.children[1].static).toBe(true) // second
-    // text node inside li
-    expect(ast.children[0].children[0].static).toBe(true) // first
-    expect(ast.children[1].children[0].static).toBe(true) // second
-  })
-
-  it('nested complex elements', () => {
-    const ast = parse(
-      '<ul><li>{{msg1}}</li><li>---</li><li>{{msg2}}</li></ul>',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    // ul
-    expect(ast.static).toBe(false) // ul
-    // li
-    expect(ast.children[0].static).toBe(false) // first
-    expect(ast.children[1].static).toBe(true) // second
-    expect(ast.children[2].static).toBe(false) // third
-    // text node inside li
-    expect(ast.children[0].children[0].static).toBe(false) // first
-    expect(ast.children[1].children[0].static).toBe(true) // second
-    expect(ast.children[2].children[0].static).toBe(false) // third
-  })
-
-  it('v-if directive', () => {
-    const ast = parse(
-      '<div id="section1" v-if="show"><p><span>hello world</span></p></div>',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false)
-    expect(ast.children[0].static).toBe(true)
-  })
-
-  it('v-else directive', () => {
-    const ast = parse(
-      '<div><p v-if="show">hello world</p><div v-else><p><span>foo bar</span></p></div></div>',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false)
-    expect(ast.children[0].static).toBe(false)
-    expect(ast.children[0].ifConditions[0].block.static).toBe(false)
-    expect(ast.children[0].ifConditions[1].block.static).toBe(false)
-    expect(ast.children[0].ifConditions[0].block.children[0].static).toBe(true)
-    expect(ast.children[0].ifConditions[1].block.children[0].static).toBe(true)
-  })
-
-  it('v-pre directive', () => {
-    const ast = parse(
-      '<ul v-pre><li>{{msg}}</li><li>world</li></ul>',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(true)
-    expect(ast.staticRoot).toBe(true)
-    expect(ast.children[0].static).toBe(true)
-    expect(ast.children[1].static).toBe(true)
-    expect(ast.children[0].children[0].static).toBe(true)
-    expect(ast.children[1].children[0].static).toBe(true)
-  })
-
-  it('v-for directive', () => {
-    const ast = parse(
-      '<ul><li v-for="item in items">hello world {{$index}}</li></ul>',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    // ul
-    expect(ast.static).toBe(false)
-    // li with v-for
-    expect(ast.children[0].static).toBe(false)
-    expect(ast.children[0].children[0].static).toBe(false)
-  })
-
-  it('v-once directive', () => {
-    const ast = parse('<p v-once>{{msg}}</p>', baseOptions)
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false) // p
-    expect(ast.children[0].static).toBe(false) // text node
-  })
-
-  it('single slot', () => {
-    const ast = parse('<div><slot>hello</slot></div>', baseOptions)
-    optimize(ast, baseOptions)
-    expect(ast.children[0].static).toBe(false) // slot
-    expect(ast.children[0].children[0].static).toBe(true) // text node
-  })
-
-  it('named slot', () => {
-    const ast = parse(
-      '<div><slot name="one">hello world</slot></div>',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(ast.children[0].static).toBe(false) // slot
-    expect(ast.children[0].children[0].static).toBe(true) // text node
-  })
-
-  it('slot target', () => {
-    const ast = parse('<p slot="one">hello world</p>', baseOptions)
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false) // slot
-    expect(ast.children[0].static).toBe(true) // text node
-  })
-
-  it('component', () => {
-    const ast = parse('<my-component></my-component>', baseOptions)
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false) // component
-  })
-
-  it('component for inline-template', () => {
-    const ast = parse(
-      '<my-component inline-template><p>hello world</p><p>{{msg}}</p></my-component>',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    // component
-    expect(ast.static).toBe(false) // component
-    // p
-    expect(ast.children[0].static).toBe(true) // first
-    expect(ast.children[1].static).toBe(false) // second
-    // text node inside p
-    expect(ast.children[0].children[0].static).toBe(true) // first
-    expect(ast.children[1].children[0].static).toBe(false) // second
-  })
-
-  it('class binding', () => {
-    const ast = parse('<p :class="class1">hello world</p>', baseOptions)
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false)
-    expect(ast.children[0].static).toBe(true)
-  })
-
-  it('style binding', () => {
-    const ast = parse('<p :style="error">{{msg}}</p>', baseOptions)
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false)
-    expect(ast.children[0].static).toBe(false)
-  })
-
-  it('key', () => {
-    const ast = parse('<p key="foo">hello world</p>', baseOptions)
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false)
-    expect(ast.children[0].static).toBe(true)
-  })
-
-  it('ref', () => {
-    const ast = parse('<p ref="foo">hello world</p>', baseOptions)
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false)
-    expect(ast.children[0].static).toBe(true)
-  })
-
-  it('transition', () => {
-    const ast = parse(
-      '<p v-if="show" transition="expand">hello world</p>',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false)
-    expect(ast.children[0].static).toBe(true)
-  })
-
-  it('v-bind directive', () => {
-    const ast = parse(
-      '<input type="text" name="field1" :value="msg">',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false)
-  })
-
-  it('v-on directive', () => {
-    const ast = parse(
-      '<input type="text" name="field1" :value="msg" @input="onInput">',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false)
-  })
-
-  it('custom directive', () => {
-    const ast = parse(
-      '<form><input type="text" name="field1" :value="msg" v-validate:field1="required"></form>',
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(ast.static).toBe(false)
-    expect(ast.children[0].static).toBe(false)
-  })
-
-  it('not root ast', () => {
-    const ast = null
-    optimize(ast, baseOptions)
-    expect(ast).toBe(null)
-  })
-
-  it('not specified isReservedTag option', () => {
-    const ast = parse('<h1 id="section1">hello world</h1>', baseOptions)
-    optimize(ast, {})
-    expect(ast.static).toBe(false)
-  })
-
-  it('mark static trees inside v-for', () => {
-    const ast = parse(
-      `<div><div v-for="i in 10"><p><span>hi</span></p></div></div>`,
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(ast.children[0].children[0].staticRoot).toBe(true)
-    expect(ast.children[0].children[0].staticInFor).toBe(true)
-  })
-
-  it('mark static trees inside v-for with nested v-else and v-once', () => {
-    const ast = parse(
-      `
-      <div v-if="1"></div>
-      <div v-else-if="2">
-        <div v-for="i in 10" :key="i">
-          <div v-if="1">{{ i }}</div>
-          <div v-else-if="2" v-once>{{ i }}</div>
-          <div v-else v-once>{{ i }}</div>
-        </div>
-      </div>
-      <div v-else>
-        <div v-for="i in 10" :key="i">
-          <div v-if="1">{{ i }}</div>
-          <div v-else v-once>{{ i }}</div>
-        </div>
-      </div>
-      `,
-      baseOptions
-    )
-    optimize(ast, baseOptions)
-    expect(
-      ast.ifConditions[1].block.children[0].children[0].ifConditions[1].block
-        .staticRoot
-    ).toBe(false)
-    expect(
-      ast.ifConditions[1].block.children[0].children[0].ifConditions[1].block
-        .staticInFor
-    ).toBe(true)
-
-    expect(
-      ast.ifConditions[1].block.children[0].children[0].ifConditions[2].block
-        .staticRoot
-    ).toBe(false)
-    expect(
-      ast.ifConditions[1].block.children[0].children[0].ifConditions[2].block
-        .staticInFor
-    ).toBe(true)
-
-    expect(
-      ast.ifConditions[2].block.children[0].children[0].ifConditions[1].block
-        .staticRoot
-    ).toBe(false)
-    expect(
-      ast.ifConditions[2].block.children[0].children[0].ifConditions[1].block
-        .staticInFor
-    ).toBe(true)
-  })
-})
diff --git a/test/unit/modules/compiler/parser.spec.ts b/test/unit/modules/compiler/parser.spec.ts
deleted file mode 100644
index 1efba124146..00000000000
--- a/test/unit/modules/compiler/parser.spec.ts
+++ /dev/null
@@ -1,1149 +0,0 @@
-import { parse } from 'compiler/parser/index'
-import { extend } from 'shared/util'
-import { baseOptions } from 'web/compiler/options'
-import { isIE, isEdge } from 'core/util/env'
-
-describe('parser', () => {
-  it('simple element', () => {
-    const ast = parse('<h1>hello world</h1>', baseOptions)
-    expect(ast.tag).toBe('h1')
-    expect(ast.plain).toBe(true)
-    expect(ast.children[0].text).toBe('hello world')
-  })
-
-  it('interpolation in element', () => {
-    const ast = parse('<h1>{{msg}}</h1>', baseOptions)
-    expect(ast.tag).toBe('h1')
-    expect(ast.plain).toBe(true)
-    expect(ast.children[0].expression).toBe('_s(msg)')
-  })
-
-  it('child elements', () => {
-    const ast = parse('<ul><li>hello world</li></ul>', baseOptions)
-    expect(ast.tag).toBe('ul')
-    expect(ast.plain).toBe(true)
-    expect(ast.children[0].tag).toBe('li')
-    expect(ast.children[0].plain).toBe(true)
-    expect(ast.children[0].children[0].text).toBe('hello world')
-    expect(ast.children[0].parent).toBe(ast)
-  })
-
-  it('unary element', () => {
-    const ast = parse('<hr>', baseOptions)
-    expect(ast.tag).toBe('hr')
-    expect(ast.plain).toBe(true)
-    expect(ast.children.length).toBe(0)
-  })
-
-  it('svg element', () => {
-    const ast = parse('<svg><text>hello world</text></svg>', baseOptions)
-    expect(ast.tag).toBe('svg')
-    expect(ast.ns).toBe('svg')
-    expect(ast.plain).toBe(true)
-    expect(ast.children[0].tag).toBe('text')
-    expect(ast.children[0].children[0].text).toBe('hello world')
-    expect(ast.children[0].parent).toBe(ast)
-  })
-
-  it('camelCase element', () => {
-    const ast = parse(
-      '<MyComponent><p>hello world</p></MyComponent>',
-      baseOptions
-    )
-    expect(ast.tag).toBe('MyComponent')
-    expect(ast.plain).toBe(true)
-    expect(ast.children[0].tag).toBe('p')
-    expect(ast.children[0].plain).toBe(true)
-    expect(ast.children[0].children[0].text).toBe('hello world')
-    expect(ast.children[0].parent).toBe(ast)
-  })
-
-  it('forbidden element', () => {
-    // style
-    const styleAst = parse('<style>error { color: red; }</style>', baseOptions)
-    expect(styleAst.tag).toBe('style')
-    expect(styleAst.plain).toBe(true)
-    expect(styleAst.forbidden).toBe(true)
-    expect(styleAst.children[0].text).toBe('error { color: red; }')
-    expect(
-      'Templates should only be responsible for mapping the state'
-    ).toHaveBeenWarned()
-    // script
-    const scriptAst = parse(
-      '<script type="text/javascript">alert("hello world!")</script>',
-      baseOptions
-    )
-    expect(scriptAst.tag).toBe('script')
-    expect(scriptAst.plain).toBe(false)
-    expect(scriptAst.forbidden).toBe(true)
-    expect(scriptAst.children[0].text).toBe('alert("hello world!")')
-    expect(
-      'Templates should only be responsible for mapping the state'
-    ).toHaveBeenWarned()
-  })
-
-  it('not contain root element', () => {
-    parse('hello world', baseOptions)
-    expect(
-      'Component template requires a root element, rather than just text'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn text before root element', () => {
-    parse('before root {{ interpolation }}<div></div>', baseOptions)
-    expect(
-      'text "before root {{ interpolation }}" outside root element will be ignored.'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn text after root element', () => {
-    parse('<div></div>after root {{ interpolation }}', baseOptions)
-    expect(
-      'text "after root {{ interpolation }}" outside root element will be ignored.'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn multiple root elements', () => {
-    parse('<div></div><div></div>', baseOptions)
-    expect(
-      'Component template should contain exactly one root element'
-    ).toHaveBeenWarned()
-  })
-
-  it('remove duplicate whitespace text nodes caused by comments', () => {
-    const ast = parse(`<div><a></a> <!----> <a></a></div>`, baseOptions)
-    expect(ast.children.length).toBe(3)
-    expect(ast.children[0].tag).toBe('a')
-    expect(ast.children[1].text).toBe(' ')
-    expect(ast.children[2].tag).toBe('a')
-  })
-
-  it('remove text nodes between v-if conditions', () => {
-    const ast = parse(
-      `<div><div v-if="1"></div> <div v-else-if="2"></div> <div v-else></div> <span></span></div>`,
-      baseOptions
-    )
-    expect(ast.children.length).toBe(3)
-    expect(ast.children[0].tag).toBe('div')
-    expect(ast.children[0].ifConditions.length).toBe(3)
-    expect(ast.children[1].text).toBe(' ') // text
-    expect(ast.children[2].tag).toBe('span')
-  })
-
-  it('warn non whitespace text between v-if conditions', () => {
-    parse(`<div><div v-if="1"></div> foo <div v-else></div></div>`, baseOptions)
-    expect(
-      `text "foo" between v-if and v-else(-if) will be ignored`
-    ).toHaveBeenWarned()
-  })
-
-  it('not warn 2 root elements with v-if and v-else', () => {
-    parse('<div v-if="1"></div><div v-else></div>', baseOptions)
-    expect(
-      'Component template should contain exactly one root element'
-    ).not.toHaveBeenWarned()
-  })
-
-  it('not warn 3 root elements with v-if, v-else-if and v-else', () => {
-    parse(
-      '<div v-if="1"></div><div v-else-if="2"></div><div v-else></div>',
-      baseOptions
-    )
-    expect(
-      'Component template should contain exactly one root element'
-    ).not.toHaveBeenWarned()
-  })
-
-  it('not warn 2 root elements with v-if and v-else on separate lines', () => {
-    parse(
-      `
-      <div v-if="1"></div>
-      <div v-else></div>
-    `,
-      baseOptions
-    )
-    expect(
-      'Component template should contain exactly one root element'
-    ).not.toHaveBeenWarned()
-  })
-
-  it('not warn 3 or more root elements with v-if, v-else-if and v-else on separate lines', () => {
-    parse(
-      `
-      <div v-if="1"></div>
-      <div v-else-if="2"></div>
-      <div v-else></div>
-    `,
-      baseOptions
-    )
-    expect(
-      'Component template should contain exactly one root element'
-    ).not.toHaveBeenWarned()
-
-    parse(
-      `
-      <div v-if="1"></div>
-      <div v-else-if="2"></div>
-      <div v-else-if="3"></div>
-      <div v-else-if="4"></div>
-      <div v-else></div>
-    `,
-      baseOptions
-    )
-    expect(
-      'Component template should contain exactly one root element'
-    ).not.toHaveBeenWarned()
-  })
-
-  it('generate correct ast for 2 root elements with v-if and v-else on separate lines', () => {
-    const ast = parse(
-      `
-      <div v-if="1"></div>
-      <p v-else></p>
-    `,
-      baseOptions
-    )
-    expect(ast.tag).toBe('div')
-    expect(ast.ifConditions[1].block.tag).toBe('p')
-  })
-
-  it('generate correct ast for 3 or more root elements with v-if and v-else on separate lines', () => {
-    const ast = parse(
-      `
-      <div v-if="1"></div>
-      <span v-else-if="2"></span>
-      <p v-else></p>
-    `,
-      baseOptions
-    )
-    expect(ast.tag).toBe('div')
-    expect(ast.ifConditions[0].block.tag).toBe('div')
-    expect(ast.ifConditions[1].block.tag).toBe('span')
-    expect(ast.ifConditions[2].block.tag).toBe('p')
-
-    const astMore = parse(
-      `
-      <div v-if="1"></div>
-      <span v-else-if="2"></span>
-      <div v-else-if="3"></div>
-      <span v-else-if="4"></span>
-      <p v-else></p>
-    `,
-      baseOptions
-    )
-    expect(astMore.tag).toBe('div')
-    expect(astMore.ifConditions[0].block.tag).toBe('div')
-    expect(astMore.ifConditions[1].block.tag).toBe('span')
-    expect(astMore.ifConditions[2].block.tag).toBe('div')
-    expect(astMore.ifConditions[3].block.tag).toBe('span')
-    expect(astMore.ifConditions[4].block.tag).toBe('p')
-  })
-
-  it('warn 2 root elements with v-if', () => {
-    parse('<div v-if="1"></div><div v-if="2"></div>', baseOptions)
-    expect(
-      'Component template should contain exactly one root element'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn 3 root elements with v-if and v-else on first 2', () => {
-    parse('<div v-if="1"></div><div v-else></div><div></div>', baseOptions)
-    expect(
-      'Component template should contain exactly one root element'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn 3 root elements with v-if and v-else-if on first 2', () => {
-    parse('<div v-if="1"></div><div v-else-if></div><div></div>', baseOptions)
-    expect(
-      'Component template should contain exactly one root element'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn 4 root elements with v-if, v-else-if and v-else on first 2', () => {
-    parse(
-      '<div v-if="1"></div><div v-else-if></div><div v-else></div><div></div>',
-      baseOptions
-    )
-    expect(
-      'Component template should contain exactly one root element'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn 2 root elements with v-if and v-else with v-for on 2nd', () => {
-    parse(
-      '<div v-if="1"></div><div v-else v-for="i in [1]"></div>',
-      baseOptions
-    )
-    expect(
-      'Cannot use v-for on stateful component root element because it renders multiple elements'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn 2 root elements with v-if and v-else-if with v-for on 2nd', () => {
-    parse(
-      '<div v-if="1"></div><div v-else-if="2" v-for="i in [1]"></div>',
-      baseOptions
-    )
-    expect(
-      'Cannot use v-for on stateful component root element because it renders multiple elements'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn <template> as root element', () => {
-    parse('<template></template>', baseOptions)
-    expect('Cannot use <template> as component root element').toHaveBeenWarned()
-  })
-
-  it('warn <slot> as root element', () => {
-    parse('<slot></slot>', baseOptions)
-    expect('Cannot use <slot> as component root element').toHaveBeenWarned()
-  })
-
-  it('warn v-for on root element', () => {
-    parse('<div v-for="item in items"></div>', baseOptions)
-    expect(
-      'Cannot use v-for on stateful component root element'
-    ).toHaveBeenWarned()
-  })
-
-  it('warn <template> key', () => {
-    parse(
-      '<div><template v-for="i in 10" :key="i"></template></div>',
-      baseOptions
-    )
-    expect('<template> cannot be keyed').toHaveBeenWarned()
-  })
-
-  it('warn the child of the <transition-group> component has sequential index', () => {
-    parse(
-      `
-      <div>
-        <transition-group>
-          <i v-for="(o, i) of arr" :key="i"></i>
-        </transition-group>
-      </div>
-    `,
-      baseOptions
-    )
-    expect(
-      'Do not use v-for index as key on <transition-group> children'
-    ).toHaveBeenWarned()
-  })
-
-  it('v-pre directive', () => {
-    const ast = parse(
-      '<div v-pre id="message1"><p>{{msg}}</p></div>',
-      baseOptions
-    )
-    expect(ast.pre).toBe(true)
-    expect(ast.attrs[0].name).toBe('id')
-    expect(ast.attrs[0].value).toBe('"message1"')
-    expect(ast.children[0].children[0].text).toBe('{{msg}}')
-  })
-
-  it('v-pre directive should leave template in DOM', () => {
-    const ast = parse(
-      '<div v-pre id="message1"><template id="template1"><p>{{msg}}</p></template></div>',
-      baseOptions
-    )
-    expect(ast.pre).toBe(true)
-    expect(ast.attrs[0].name).toBe('id')
-    expect(ast.attrs[0].value).toBe('"message1"')
-    expect(ast.children[0].attrs[0].name).toBe('id')
-    expect(ast.children[0].attrs[0].value).toBe('"template1"')
-  })
-
-  it('v-for directive basic syntax', () => {
-    const ast = parse('<ul><li v-for="item in items"></li></ul>', baseOptions)
-    const liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('item')
-  })
-
-  it('v-for directive iteration syntax', () => {
-    const ast = parse(
-      '<ul><li v-for="(item, index) in items"></li></ul>',
-      baseOptions
-    )
-    const liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('item')
-    expect(liAst.iterator1).toBe('index')
-    expect(liAst.iterator2).toBeUndefined()
-  })
-
-  it('v-for directive iteration syntax (multiple)', () => {
-    const ast = parse(
-      '<ul><li v-for="(item, key, index) in items"></li></ul>',
-      baseOptions
-    )
-    const liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('item')
-    expect(liAst.iterator1).toBe('key')
-    expect(liAst.iterator2).toBe('index')
-  })
-
-  it('v-for directive key', () => {
-    const ast = parse(
-      '<ul><li v-for="item in items" :key="item.uid"></li></ul>',
-      baseOptions
-    )
-    const liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('item')
-    expect(liAst.key).toBe('item.uid')
-  })
-
-  it('v-for directive destructuring', () => {
-    let ast = parse('<ul><li v-for="{ foo } in items"></li></ul>', baseOptions)
-    let liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('{ foo }')
-
-    // with paren
-    ast = parse('<ul><li v-for="({ foo }) in items"></li></ul>', baseOptions)
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('{ foo }')
-
-    // multi-var destructuring
-    ast = parse(
-      '<ul><li v-for="{ foo, bar, baz } in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('{ foo, bar, baz }')
-
-    // multi-var destructuring with paren
-    ast = parse(
-      '<ul><li v-for="({ foo, bar, baz }) in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('{ foo, bar, baz }')
-
-    // with index
-    ast = parse('<ul><li v-for="({ foo }, i) in items"></li></ul>', baseOptions)
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('{ foo }')
-    expect(liAst.iterator1).toBe('i')
-
-    // with key + index
-    ast = parse(
-      '<ul><li v-for="({ foo }, i, j) in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('{ foo }')
-    expect(liAst.iterator1).toBe('i')
-    expect(liAst.iterator2).toBe('j')
-
-    // multi-var destructuring with index
-    ast = parse(
-      '<ul><li v-for="({ foo, bar, baz }, i) in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('{ foo, bar, baz }')
-    expect(liAst.iterator1).toBe('i')
-
-    // array
-    ast = parse('<ul><li v-for="[ foo ] in items"></li></ul>', baseOptions)
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('[ foo ]')
-
-    // multi-array
-    ast = parse(
-      '<ul><li v-for="[ foo, bar, baz ] in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('[ foo, bar, baz ]')
-
-    // array with paren
-    ast = parse('<ul><li v-for="([ foo ]) in items"></li></ul>', baseOptions)
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('[ foo ]')
-
-    // multi-array with paren
-    ast = parse(
-      '<ul><li v-for="([ foo, bar, baz ]) in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('[ foo, bar, baz ]')
-
-    // array with index
-    ast = parse('<ul><li v-for="([ foo ], i) in items"></li></ul>', baseOptions)
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('[ foo ]')
-    expect(liAst.iterator1).toBe('i')
-
-    // array with key + index
-    ast = parse(
-      '<ul><li v-for="([ foo ], i, j) in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('[ foo ]')
-    expect(liAst.iterator1).toBe('i')
-    expect(liAst.iterator2).toBe('j')
-
-    // multi-array with paren
-    ast = parse(
-      '<ul><li v-for="([ foo, bar, baz ]) in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('[ foo, bar, baz ]')
-
-    // multi-array with index
-    ast = parse(
-      '<ul><li v-for="([ foo, bar, baz ], i) in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('[ foo, bar, baz ]')
-    expect(liAst.iterator1).toBe('i')
-
-    // nested
-    ast = parse(
-      '<ul><li v-for="({ foo, bar: { baz }, qux: [ n ] }, i, j) in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('{ foo, bar: { baz }, qux: [ n ] }')
-    expect(liAst.iterator1).toBe('i')
-    expect(liAst.iterator2).toBe('j')
-
-    // array nested
-    ast = parse(
-      '<ul><li v-for="([ foo, { bar }, baz ], i, j) in items"></li></ul>',
-      baseOptions
-    )
-    liAst = ast.children[0]
-    expect(liAst.for).toBe('items')
-    expect(liAst.alias).toBe('[ foo, { bar }, baz ]')
-    expect(liAst.iterator1).toBe('i')
-    expect(liAst.iterator2).toBe('j')
-  })
-
-  it('v-for directive invalid syntax', () => {
-    parse('<ul><li v-for="item into items"></li></ul>', baseOptions)
-    expect('Invalid v-for expression').toHaveBeenWarned()
-  })
-
-  it('v-if directive syntax', () => {
-    const ast = parse('<p v-if="show">hello world</p>', baseOptions)
-    expect(ast.if).toBe('show')
-    expect(ast.ifConditions[0].exp).toBe('show')
-  })
-
-  it('v-else-if directive syntax', () => {
-    const ast = parse(
-      '<div><p v-if="show">hello</p><span v-else-if="2">elseif</span><p v-else>world</p></div>',
-      baseOptions
-    )
-    const ifAst = ast.children[0]
-    const conditionsAst = ifAst.ifConditions
-    expect(conditionsAst.length).toBe(3)
-    expect(conditionsAst[1].block.children[0].text).toBe('elseif')
-    expect(conditionsAst[1].block.parent).toBe(ast)
-    expect(conditionsAst[2].block.children[0].text).toBe('world')
-    expect(conditionsAst[2].block.parent).toBe(ast)
-  })
-
-  it('v-else directive syntax', () => {
-    const ast = parse(
-      '<div><p v-if="show">hello</p><p v-else>world</p></div>',
-      baseOptions
-    )
-    const ifAst = ast.children[0]
-    const conditionsAst = ifAst.ifConditions
-    expect(conditionsAst.length).toBe(2)
-    expect(conditionsAst[1].block.children[0].text).toBe('world')
-    expect(conditionsAst[1].block.parent).toBe(ast)
-  })
-
-  it('v-else-if directive invalid syntax', () => {
-    parse('<div><p v-else-if="1">world</p></div>', baseOptions)
-    expect('v-else-if="1" used on element').toHaveBeenWarned()
-  })
-
-  it('v-else directive invalid syntax', () => {
-    parse('<div><p v-else>world</p></div>', baseOptions)
-    expect('v-else used on element').toHaveBeenWarned()
-  })
-
-  it('v-once directive syntax', () => {
-    const ast = parse('<p v-once>world</p>', baseOptions)
-    expect(ast.once).toBe(true)
-  })
-
-  it('slot tag single syntax', () => {
-    const ast = parse('<div><slot></slot></div>', baseOptions)
-    expect(ast.children[0].tag).toBe('slot')
-    expect(ast.children[0].slotName).toBeUndefined()
-  })
-
-  it('slot tag named syntax', () => {
-    const ast = parse(
-      '<div><slot name="one">hello world</slot></div>',
-      baseOptions
-    )
-    expect(ast.children[0].tag).toBe('slot')
-    expect(ast.children[0].slotName).toBe('"one"')
-  })
-
-  it('slot target', () => {
-    const ast = parse('<p slot="one">hello world</p>', baseOptions)
-    expect(ast.slotTarget).toBe('"one"')
-  })
-
-  it('component properties', () => {
-    const ast = parse('<my-component :msg="hello"></my-component>', baseOptions)
-    expect(ast.attrs[0].name).toBe('msg')
-    expect(ast.attrs[0].value).toBe('hello')
-  })
-
-  it('component "is" attribute', () => {
-    const ast = parse(
-      '<my-component is="component1"></my-component>',
-      baseOptions
-    )
-    expect(ast.component).toBe('"component1"')
-  })
-
-  it('component "inline-template" attribute', () => {
-    const ast = parse(
-      '<my-component inline-template>hello world</my-component>',
-      baseOptions
-    )
-    expect(ast.inlineTemplate).toBe(true)
-  })
-
-  it('class binding', () => {
-    // static
-    const ast1 = parse('<p class="class1">hello world</p>', baseOptions)
-    expect(ast1.staticClass).toBe('"class1"')
-    // dynamic
-    const ast2 = parse('<p :class="class1">hello world</p>', baseOptions)
-    expect(ast2.classBinding).toBe('class1')
-    // interpolation warning
-    parse('<p class="{{error}}">hello world</p>', baseOptions)
-    expect(
-      'Interpolation inside attributes has been removed'
-    ).toHaveBeenWarned()
-  })
-
-  it('style binding', () => {
-    const ast = parse('<p :style="error">hello world</p>', baseOptions)
-    expect(ast.styleBinding).toBe('error')
-  })
-
-  it('attribute with v-bind', () => {
-    const ast = parse(
-      '<input type="text" name="field1" :value="msg">',
-      baseOptions
-    )
-    expect(ast.attrsList[0].name).toBe('type')
-    expect(ast.attrsList[0].value).toBe('text')
-    expect(ast.attrsList[1].name).toBe('name')
-    expect(ast.attrsList[1].value).toBe('field1')
-    expect(ast.attrsMap['type']).toBe('text')
-    expect(ast.attrsMap['name']).toBe('field1')
-    expect(ast.attrs[0].name).toBe('type')
-    expect(ast.attrs[0].value).toBe('"text"')
-    expect(ast.attrs[1].name).toBe('name')
-    expect(ast.attrs[1].value).toBe('"field1"')
-    expect(ast.props[0].name).toBe('value')
-    expect(ast.props[0].value).toBe('msg')
-  })
-
-  it('empty v-bind expression', () => {
-    parse('<div :empty-msg=""></div>', baseOptions)
-    expect(
-      'The value for a v-bind expression cannot be empty. Found in "v-bind:empty-msg"'
-    ).toHaveBeenWarned()
-  })
-
-  if (process.env.VBIND_PROP_SHORTHAND) {
-    it('v-bind.prop shorthand syntax', () => {
-      const ast = parse('<div .id="foo"></div>', baseOptions)
-      expect(ast.props).toEqual([{ name: 'id', value: 'foo', dynamic: false }])
-    })
-
-    it('v-bind.prop shorthand syntax w/ modifiers', () => {
-      const ast = parse('<div .id.mod="foo"></div>', baseOptions)
-      expect(ast.props).toEqual([{ name: 'id', value: 'foo', dynamic: false }])
-    })
-
-    it('v-bind.prop shorthand dynamic argument', () => {
-      const ast = parse('<div .[id]="foo"></div>', baseOptions)
-      expect(ast.props).toEqual([{ name: 'id', value: 'foo', dynamic: true }])
-    })
-  }
-
-  // This only works for string templates.
-  // In-DOM templates will be malformed before Vue can parse it.
-  describe('parse and warn invalid dynamic arguments', () => {
-    ;[
-      `<div v-bind:['foo' + bar]="baz"/>`,
-      `<div :['foo' + bar]="baz"/>`,
-      `<div @['foo' + bar]="baz"/>`,
-      `<foo #['foo' + bar]="baz"/>`,
-      `<div :['foo' + bar].some.mod="baz"/>`
-    ].forEach(template => {
-      it(template, () => {
-        const ast = parse(template, baseOptions)
-        expect(`Invalid dynamic argument expression`).toHaveBeenWarned()
-      })
-    })
-  })
-
-  // #9781
-  it('multiple dynamic slot names without warning', () => {
-    const ast = parse(
-      `<my-component>
-      <template #[foo]>foo</template>
-      <template #[data]="scope">scope</template>
-      <template #[bar]>bar</template>
-    </my-component>`,
-      baseOptions
-    )
-
-    expect(`Invalid dynamic argument expression`).not.toHaveBeenWarned()
-    expect(ast.scopedSlots.foo).not.toBeUndefined()
-    expect(ast.scopedSlots.data).not.toBeUndefined()
-    expect(ast.scopedSlots.bar).not.toBeUndefined()
-    expect(ast.scopedSlots.foo.type).toBe(1)
-    expect(ast.scopedSlots.data.type).toBe(1)
-    expect(ast.scopedSlots.bar.type).toBe(1)
-    expect(ast.scopedSlots.foo.attrsMap['#[foo]']).toBe('')
-    expect(ast.scopedSlots.bar.attrsMap['#[bar]']).toBe('')
-    expect(ast.scopedSlots.data.attrsMap['#[data]']).toBe('scope')
-  })
-
-  // #6887
-  it('special case static attribute that must be props', () => {
-    const ast = parse('<video muted></video>', baseOptions)
-    expect(ast.attrs[0].name).toBe('muted')
-    expect(ast.attrs[0].value).toBe('""')
-    expect(ast.props[0].name).toBe('muted')
-    expect(ast.props[0].value).toBe('true')
-  })
-
-  it('attribute with v-on', () => {
-    const ast = parse(
-      '<input type="text" name="field1" :value="msg" @input="onInput">',
-      baseOptions
-    )
-    expect(ast.events.input.value).toBe('onInput')
-  })
-
-  it('attribute with directive', () => {
-    const ast = parse(
-      '<input type="text" name="field1" :value="msg" v-validate:field1="required">',
-      baseOptions
-    )
-    expect(ast.directives[0].name).toBe('validate')
-    expect(ast.directives[0].value).toBe('required')
-    expect(ast.directives[0].arg).toBe('field1')
-  })
-
-  it('attribute with modified directive', () => {
-    const ast = parse(
-      '<input type="text" name="field1" :value="msg" v-validate.on.off>',
-      baseOptions
-    )
-    expect(ast.directives[0].modifiers.on).toBe(true)
-    expect(ast.directives[0].modifiers.off).toBe(true)
-  })
-
-  it('literal attribute', () => {
-    // basic
-    const ast1 = parse(
-      '<input type="text" name="field1" value="hello world">',
-      baseOptions
-    )
-    expect(ast1.attrsList[0].name).toBe('type')
-    expect(ast1.attrsList[0].value).toBe('text')
-    expect(ast1.attrsList[1].name).toBe('name')
-    expect(ast1.attrsList[1].value).toBe('field1')
-    expect(ast1.attrsList[2].name).toBe('value')
-    expect(ast1.attrsList[2].value).toBe('hello world')
-    expect(ast1.attrsMap['type']).toBe('text')
-    expect(ast1.attrsMap['name']).toBe('field1')
-    expect(ast1.attrsMap['value']).toBe('hello world')
-    expect(ast1.attrs[0].name).toBe('type')
-    expect(ast1.attrs[0].value).toBe('"text"')
-    expect(ast1.attrs[1].name).toBe('name')
-    expect(ast1.attrs[1].value).toBe('"field1"')
-    expect(ast1.attrs[2].name).toBe('value')
-    expect(ast1.attrs[2].value).toBe('"hello world"')
-    // interpolation warning
-    parse('<input type="text" name="field1" value="{{msg}}">', baseOptions)
-    expect(
-      'Interpolation inside attributes has been removed'
-    ).toHaveBeenWarned()
-  })
-
-  if (!isIE && !isEdge) {
-    it('duplicate attribute', () => {
-      parse('<p class="class1" class="class1">hello world</p>', baseOptions)
-      expect('duplicate attribute').toHaveBeenWarned()
-    })
-  }
-
-  it('custom delimiter', () => {
-    const ast = parse(
-      '<p>{msg}</p>',
-      extend({ delimiters: ['{', '}'] }, baseOptions)
-    )
-    expect(ast.children[0].expression).toBe('_s(msg)')
-  })
-
-  it('not specified getTagNamespace option', () => {
-    const options = extend({}, baseOptions)
-    delete options.getTagNamespace
-    const ast = parse('<svg><text>hello world</text></svg>', options)
-    expect(ast.tag).toBe('svg')
-    expect(ast.ns).toBeUndefined()
-  })
-
-  it('not specified mustUseProp', () => {
-    const options = extend({}, baseOptions)
-    delete options.mustUseProp
-    const ast = parse('<input type="text" name="field1" :value="msg">', options)
-    expect(ast.props).toBeUndefined()
-  })
-
-  it('use prop when prop modifier was explicitly declared', () => {
-    const ast = parse(
-      '<component is="textarea" :value.prop="val" />',
-      baseOptions
-    )
-    expect(ast.attrs).toBeUndefined()
-    expect(ast.props.length).toBe(1)
-    expect(ast.props[0].name).toBe('value')
-    expect(ast.props[0].value).toBe('val')
-  })
-
-  it('pre/post transforms', () => {
-    const options = extend({}, baseOptions)
-    const spy1 = vi.fn()
-    const spy2 = vi.fn()
-    options.modules = options.modules.concat([
-      {
-        preTransformNode(el) {
-          spy1(el.tag)
-        },
-        postTransformNode(el) {
-          expect(el.attrs.length).toBe(1)
-          spy2(el.tag)
-        }
-      }
-    ])
-    parse('<img v-pre src="hi">', options)
-    expect(spy1).toHaveBeenCalledWith('img')
-    expect(spy2).toHaveBeenCalledWith('img')
-  })
-
-  it('preserve whitespace in <pre> tag', function () {
-    const options = extend({}, baseOptions)
-    const ast = parse(
-      '<pre><code>  \n<span>hi</span>\n  </code><span> </span></pre>',
-      options
-    )
-    const code = ast.children[0]
-    expect(code.children[0].type).toBe(3)
-    expect(code.children[0].text).toBe('  \n')
-    expect(code.children[2].type).toBe(3)
-    expect(code.children[2].text).toBe('\n  ')
-
-    const span = ast.children[1]
-    expect(span.children[0].type).toBe(3)
-    expect(span.children[0].text).toBe(' ')
-  })
-
-  // #5992
-  it('ignore the first newline in <pre> tag', function () {
-    const options = extend({}, baseOptions)
-    const ast = parse(
-      '<div><pre>\nabc</pre>\ndef<pre>\n\nabc</pre></div>',
-      options
-    )
-    const pre = ast.children[0]
-    expect(pre.children[0].type).toBe(3)
-    expect(pre.children[0].text).toBe('abc')
-    const text = ast.children[1]
-    expect(text.type).toBe(3)
-    expect(text.text).toBe('\ndef')
-    const pre2 = ast.children[2]
-    expect(pre2.children[0].type).toBe(3)
-    expect(pre2.children[0].text).toBe('\nabc')
-  })
-
-  it('keep first newline after unary tag in <pre>', () => {
-    const options = extend({}, baseOptions)
-    const ast = parse('<pre>abc<input>\ndef</pre>', options)
-    expect(ast.children[1].type).toBe(1)
-    expect(ast.children[1].tag).toBe('input')
-    expect(ast.children[2].type).toBe(3)
-    expect(ast.children[2].text).toBe('\ndef')
-  })
-
-  it('forgivingly handle < in plain text', () => {
-    const options = extend({}, baseOptions)
-    const ast = parse('<p>1 < 2 < 3</p>', options)
-    expect(ast.tag).toBe('p')
-    expect(ast.children.length).toBe(1)
-    expect(ast.children[0].type).toBe(3)
-    expect(ast.children[0].text).toBe('1 < 2 < 3')
-  })
-
-  it('IE conditional comments', () => {
-    const options = extend({}, baseOptions)
-    const ast = parse(
-      `
-      <div>
-        <!--[if lte IE 8]>
-          <p>Test 1</p>
-        <![endif]-->
-      </div>
-    `,
-      options
-    )
-    expect(ast.tag).toBe('div')
-    expect(ast.children.length).toBe(0)
-  })
-
-  it('parse content in textarea as text', () => {
-    const options = extend({}, baseOptions)
-
-    const whitespace = parse(
-      `
-      <textarea>
-        <p>Test 1</p>
-        test2
-      </textarea>
-    `,
-      options
-    )
-    expect(whitespace.tag).toBe('textarea')
-    expect(whitespace.children.length).toBe(1)
-    expect(whitespace.children[0].type).toBe(3)
-    // textarea is whitespace sensitive
-    expect(whitespace.children[0].text).toBe(`        <p>Test 1</p>
-        test2
-      `)
-
-    const comment = parse('<textarea><!--comment--></textarea>', options)
-    expect(comment.tag).toBe('textarea')
-    expect(comment.children.length).toBe(1)
-    expect(comment.children[0].type).toBe(3)
-    expect(comment.children[0].text).toBe('<!--comment-->')
-  })
-
-  // #5526
-  it('should not decode text in script tags', () => {
-    const options = extend({}, baseOptions)
-    const ast = parse(
-      `<script type="x/template">&gt;<foo>&lt;</script>`,
-      options
-    )
-    expect(ast.children[0].text).toBe(`&gt;<foo>&lt;`)
-  })
-
-  it('should ignore comments', () => {
-    const options = extend({}, baseOptions)
-    const ast = parse(`<div>123<!--comment here--></div>`, options)
-    expect(ast.tag).toBe('div')
-    expect(ast.children.length).toBe(1)
-    expect(ast.children[0].type).toBe(3)
-    expect(ast.children[0].text).toBe('123')
-  })
-
-  it('should kept comments', () => {
-    const options = extend(
-      {
-        comments: true
-      },
-      baseOptions
-    )
-    const ast = parse(`<div>123<!--comment here--></div>`, options)
-    expect(ast.tag).toBe('div')
-    expect(ast.children.length).toBe(2)
-    expect(ast.children[0].type).toBe(3)
-    expect(ast.children[0].text).toBe('123')
-    expect(ast.children[1].type).toBe(3) // parse comment with ASTText
-    expect(ast.children[1].isComment).toBe(true) // parse comment with ASTText
-    expect(ast.children[1].text).toBe('comment here')
-  })
-
-  // #9407
-  it('should parse templates with comments anywhere', () => {
-    const options = extend(
-      {
-        comments: true
-      },
-      baseOptions
-    )
-    const ast = parse(`<!--comment here--><div>123</div>`, options)
-    expect(ast.tag).toBe('div')
-    expect(ast.children.length).toBe(1)
-  })
-
-  // #8103
-  it('should allow CRLFs in string interpolations', () => {
-    const ast = parse(`<p>{{\r\nmsg\r\n}}</p>`, baseOptions)
-    expect(ast.children[0].expression).toBe('_s(msg)')
-  })
-
-  it('preserveWhitespace: false', () => {
-    const options = extend(
-      {
-        preserveWhitespace: false
-      },
-      baseOptions
-    )
-
-    const ast = parse(
-      '<p>\n  Welcome to <b>Vue.js</b>    <i>world</i>  \n  <span>.\n  Have fun!\n</span></p>',
-      options
-    )
-    expect(ast.tag).toBe('p')
-    expect(ast.children.length).toBe(4)
-    expect(ast.children[0].type).toBe(3)
-    expect(ast.children[0].text).toBe('\n  Welcome to ')
-    expect(ast.children[1].tag).toBe('b')
-    expect(ast.children[1].children[0].text).toBe('Vue.js')
-    expect(ast.children[2].tag).toBe('i')
-    expect(ast.children[2].children[0].text).toBe('world')
-    expect(ast.children[3].tag).toBe('span')
-    expect(ast.children[3].children[0].text).toBe('.\n  Have fun!\n')
-  })
-
-  const condenseOptions = extend(
-    {
-      whitespace: 'condense',
-      // should be ignored when whitespace is specified
-      preserveWhitespace: false
-    },
-    baseOptions
-  )
-
-  it(`whitespace: 'condense'`, () => {
-    const options = extend({}, condenseOptions)
-    const ast = parse(
-      '<p>\n  Welcome to <b>Vue.js</b>    <i>world</i>  \n  <span>.\n  Have fun!\n</span></p>',
-      options
-    )
-    expect(ast.tag).toBe('p')
-    expect(ast.children.length).toBe(5)
-    expect(ast.children[0].type).toBe(3)
-    expect(ast.children[0].text).toBe(' Welcome to ')
-    expect(ast.children[1].tag).toBe('b')
-    expect(ast.children[1].children[0].text).toBe('Vue.js')
-    expect(ast.children[2].type).toBe(3)
-    // should condense inline whitespace into single space
-    expect(ast.children[2].text).toBe(' ')
-    expect(ast.children[3].tag).toBe('i')
-    expect(ast.children[3].children[0].text).toBe('world')
-    // should have removed the whitespace node between tags that contains newlines
-    expect(ast.children[4].tag).toBe('span')
-    expect(ast.children[4].children[0].text).toBe('. Have fun! ')
-  })
-
-  it(`maintains &nbsp; with whitespace: 'condense'`, () => {
-    const options = extend({}, condenseOptions)
-    const ast = parse('<span>&nbsp;</span>', options)
-    const code = ast.children[0]
-    expect(code.type).toBe(3)
-    expect(code.text).toBe('\xA0')
-  })
-
-  it(`preserve whitespace in <pre> tag with whitespace: 'condense'`, function () {
-    const options = extend({}, condenseOptions)
-    const ast = parse(
-      '<pre><code>  \n<span>hi</span>\n  </code><span> </span></pre>',
-      options
-    )
-    const code = ast.children[0]
-    expect(code.children[0].type).toBe(3)
-    expect(code.children[0].text).toBe('  \n')
-    expect(code.children[2].type).toBe(3)
-    expect(code.children[2].text).toBe('\n  ')
-
-    const span = ast.children[1]
-    expect(span.children[0].type).toBe(3)
-    expect(span.children[0].text).toBe(' ')
-  })
-
-  it(`ignore the first newline in <pre> tag with whitespace: 'condense'`, function () {
-    const options = extend({}, condenseOptions)
-    const ast = parse(
-      '<div><pre>\nabc</pre>\ndef<pre>\n\nabc</pre></div>',
-      options
-    )
-    const pre = ast.children[0]
-    expect(pre.children[0].type).toBe(3)
-    expect(pre.children[0].text).toBe('abc')
-    const text = ast.children[1]
-    expect(text.type).toBe(3)
-    expect(text.text).toBe(' def')
-    const pre2 = ast.children[2]
-    expect(pre2.children[0].type).toBe(3)
-    expect(pre2.children[0].text).toBe('\nabc')
-  })
-
-  it(`keep first newline after unary tag in <pre> with whitespace: 'condense'`, () => {
-    const options = extend({}, condenseOptions)
-    const ast = parse('<pre>abc<input>\ndef</pre>', options)
-    expect(ast.children[1].type).toBe(1)
-    expect(ast.children[1].tag).toBe('input')
-    expect(ast.children[2].type).toBe(3)
-    expect(ast.children[2].text).toBe('\ndef')
-  })
-
-  // #10152
-  it('not warn when scoped slot used inside of dynamic component on regular element', () => {
-    parse(
-      `
-      <div>
-        <div is="customComp" v-slot="slotProps"></div>
-        <div :is="'customComp'" v-slot="slotProps"></div>
-        <div v-bind:is="'customComp'" v-slot="slotProps"></div>
-      </div>
-    `,
-      baseOptions
-    )
-    expect(
-      'v-slot can only be used on components or <template>'
-    ).not.toHaveBeenWarned()
-
-    parse(
-      `<div is="customComp"><template v-slot="slotProps"></template></div>`,
-      baseOptions
-    )
-    expect(
-      `<template v-slot> can only appear at the root level inside the receiving the component`
-    ).not.toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/modules/observer/dep.spec.ts b/test/unit/modules/observer/dep.spec.ts
deleted file mode 100644
index de226812caf..00000000000
--- a/test/unit/modules/observer/dep.spec.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import Dep, { cleanupDeps } from 'core/observer/dep'
-
-describe('Dep', () => {
-  let dep
-
-  beforeEach(() => {
-    dep = new Dep()
-  })
-
-  describe('instance', () => {
-    it('should be created with correct properties', () => {
-      expect(dep.subs.length).toBe(0)
-      expect(new Dep().id).toBe(dep.id + 1)
-    })
-  })
-
-  describe('addSub()', () => {
-    it('should add sub', () => {
-      dep.addSub(null)
-      expect(dep.subs.length).toBe(1)
-      expect(dep.subs[0]).toBe(null)
-    })
-  })
-
-  describe('removeSub()', () => {
-    it('should remove sub', () => {
-      const sub = {}
-      dep.subs.push(sub)
-      dep.removeSub(sub)
-      expect(dep.subs.includes(sub)).toBe(false)
-
-      // nulled subs are cleared on next flush
-      cleanupDeps()
-      expect(dep.subs.length).toBe(0)
-    })
-  })
-
-  describe('depend()', () => {
-    let _target
-
-    beforeAll(() => {
-      _target = Dep.target
-    })
-
-    afterAll(() => {
-      Dep.target = _target
-    })
-
-    it('should do nothing if no target', () => {
-      Dep.target = null
-      dep.depend()
-    })
-
-    it('should add itself to target', () => {
-      Dep.target = { addDep: vi.fn() } as any
-      dep.depend()
-      expect(Dep.target!.addDep).toHaveBeenCalledWith(dep)
-    })
-  })
-
-  describe('notify()', () => {
-    it('should notify subs', () => {
-      dep.subs.push({ update: vi.fn() })
-      dep.notify()
-      expect(dep.subs[0].update).toHaveBeenCalled()
-    })
-  })
-})
diff --git a/test/unit/modules/observer/observer.spec.ts b/test/unit/modules/observer/observer.spec.ts
deleted file mode 100644
index 72fc03e2e7e..00000000000
--- a/test/unit/modules/observer/observer.spec.ts
+++ /dev/null
@@ -1,400 +0,0 @@
-import Vue from 'vue'
-import {
-  Observer,
-  observe,
-  set as setProp,
-  del as delProp
-} from 'core/observer/index'
-import Dep from 'core/observer/dep'
-import { hasOwn } from 'core/util/index'
-
-describe('Observer', () => {
-  it('create on non-observables', () => {
-    // skip primitive value
-    const ob1 = observe(1)
-    expect(ob1).toBeUndefined()
-    // avoid vue instance
-    const ob2 = observe(new Vue())
-    expect(ob2).toBeUndefined()
-    // avoid frozen objects
-    const ob3 = observe(Object.freeze({}))
-    expect(ob3).toBeUndefined()
-  })
-
-  it('create on object', () => {
-    // on object
-    const obj: any = {
-      a: {},
-      b: {}
-    }
-    const ob1 = observe(obj)!
-    expect(ob1 instanceof Observer).toBe(true)
-    expect(ob1.value).toBe(obj)
-    expect(obj.__ob__).toBe(ob1)
-    // should've walked children
-    expect(obj.a.__ob__ instanceof Observer).toBe(true)
-    expect(obj.b.__ob__ instanceof Observer).toBe(true)
-    // should return existing ob on already observed objects
-    const ob2 = observe(obj)!
-    expect(ob2).toBe(ob1)
-  })
-
-  it('create on null', () => {
-    // on null
-    const obj: any = Object.create(null)
-    obj.a = {}
-    obj.b = {}
-    const ob1 = observe(obj)!
-    expect(ob1 instanceof Observer).toBe(true)
-    expect(ob1.value).toBe(obj)
-    expect(obj.__ob__).toBe(ob1)
-    // should've walked children
-    expect(obj.a.__ob__ instanceof Observer).toBe(true)
-    expect(obj.b.__ob__ instanceof Observer).toBe(true)
-    // should return existing ob on already observed objects
-    const ob2 = observe(obj)!
-    expect(ob2).toBe(ob1)
-  })
-
-  it('create on already observed object', () => {
-    // on object
-    const obj: any = {}
-    let val = 0
-    let getCount = 0
-    Object.defineProperty(obj, 'a', {
-      configurable: true,
-      enumerable: true,
-      get() {
-        getCount++
-        return val
-      },
-      set(v) {
-        val = v
-      }
-    })
-
-    const ob1 = observe(obj)!
-    expect(ob1 instanceof Observer).toBe(true)
-    expect(ob1.value).toBe(obj)
-    expect(obj.__ob__).toBe(ob1)
-
-    getCount = 0
-    // Each read of 'a' should result in only one get underlying get call
-    obj.a
-    expect(getCount).toBe(1)
-    obj.a
-    expect(getCount).toBe(2)
-
-    // should return existing ob on already observed objects
-    const ob2 = observe(obj)!
-    expect(ob2).toBe(ob1)
-
-    // should call underlying setter
-    obj.a = 10
-    expect(val).toBe(10)
-  })
-
-  it('create on property with only getter', () => {
-    // on object
-    const obj: any = {}
-    Object.defineProperty(obj, 'a', {
-      configurable: true,
-      enumerable: true,
-      get() {
-        return 123
-      }
-    })
-
-    const ob1 = observe(obj)!
-    expect(ob1 instanceof Observer).toBe(true)
-    expect(ob1.value).toBe(obj)
-    expect(obj.__ob__).toBe(ob1)
-
-    // should be able to read
-    expect(obj.a).toBe(123)
-
-    // should return existing ob on already observed objects
-    const ob2 = observe(obj)!
-    expect(ob2).toBe(ob1)
-
-    // since there is no setter, you shouldn't be able to write to it
-    // PhantomJS throws when a property with no setter is set
-    // but other real browsers don't
-    try {
-      obj.a = 101
-    } catch (e) {}
-    expect(obj.a).toBe(123)
-  })
-
-  it('create on property with only setter', () => {
-    // on object
-    const obj: any = {}
-    let val = 10
-    Object.defineProperty(obj, 'a', {
-      // eslint-disable-line accessor-pairs
-      configurable: true,
-      enumerable: true,
-      set(v) {
-        val = v
-      }
-    })
-
-    const ob1 = observe(obj)!
-    expect(ob1 instanceof Observer).toBe(true)
-    expect(ob1.value).toBe(obj)
-    expect(obj.__ob__).toBe(ob1)
-
-    // reads should return undefined
-    expect(obj.a).toBe(undefined)
-
-    // should return existing ob on already observed objects
-    const ob2 = observe(obj)!
-    expect(ob2).toBe(ob1)
-
-    // writes should call the set function
-    obj.a = 100
-    expect(val).toBe(100)
-  })
-
-  it('create on property which is marked not configurable', () => {
-    // on object
-    const obj: any = {}
-    Object.defineProperty(obj, 'a', {
-      configurable: false,
-      enumerable: true,
-      value: 10
-    })
-
-    const ob1 = observe(obj)!
-    expect(ob1 instanceof Observer).toBe(true)
-    expect(ob1.value).toBe(obj)
-    expect(obj.__ob__).toBe(ob1)
-  })
-
-  it('create on array', () => {
-    // on object
-    const arr: any = [{}, {}]
-    const ob1 = observe(arr)!
-    expect(ob1 instanceof Observer).toBe(true)
-    expect(ob1.value).toBe(arr)
-    expect(arr.__ob__).toBe(ob1)
-    // should've walked children
-    expect(arr[0].__ob__ instanceof Observer).toBe(true)
-    expect(arr[1].__ob__ instanceof Observer).toBe(true)
-  })
-
-  it('observing object prop change', () => {
-    const obj: any = { a: { b: 2 }, c: NaN }
-    observe(obj)!
-    // mock a watcher!
-    const watcher: any = {
-      deps: [],
-      addDep(dep) {
-        this.deps.push(dep)
-        dep.addSub(this)
-      },
-      update: vi.fn()
-    }
-    // collect dep
-    Dep.target = watcher
-    obj.a.b
-    Dep.target = null
-    expect(watcher.deps.length).toBe(3) // obj.a + a + a.b
-    obj.a.b = 3
-    expect(watcher.update.mock.calls.length).toBe(1)
-    // swap object
-    obj.a = { b: 4 }
-    expect(watcher.update.mock.calls.length).toBe(2)
-    watcher.deps = []
-
-    Dep.target = watcher
-    obj.a.b
-    obj.c
-    Dep.target = null
-    expect(watcher.deps.length).toBe(4)
-    // set on the swapped object
-    obj.a.b = 5
-    expect(watcher.update.mock.calls.length).toBe(3)
-    // should not trigger on NaN -> NaN set
-    obj.c = NaN
-    expect(watcher.update.mock.calls.length).toBe(3)
-  })
-
-  it('observing object prop change on defined property', () => {
-    const obj: any = { val: 2 }
-    Object.defineProperty(obj, 'a', {
-      configurable: true,
-      enumerable: true,
-      get() {
-        return this.val
-      },
-      set(v) {
-        this.val = v
-        // eslint-disable-next-line no-setter-return
-        return this.val
-      }
-    })
-
-    observe(obj)!
-    expect(obj.a).toBe(2) // Make sure 'this' is preserved
-    obj.a = 3
-    expect(obj.val).toBe(3) // make sure 'setter' was called
-    obj.val = 5
-    expect(obj.a).toBe(5) // make sure 'getter' was called
-  })
-
-  it('observing set/delete', () => {
-    const obj1: any = { a: 1 }
-    const ob1 = observe(obj1) as any
-    const dep1 = ob1.dep
-    vi.spyOn(dep1, 'notify')
-    setProp(obj1, 'b', 2)
-    expect(obj1.b).toBe(2)
-    expect(dep1.notify.mock.calls.length).toBe(1)
-    delProp(obj1, 'a')
-    expect(hasOwn(obj1, 'a')).toBe(false)
-    expect(dep1.notify.mock.calls.length).toBe(2)
-    // set existing key, should be a plain set and not
-    // trigger own ob's notify
-    setProp(obj1, 'b', 3)
-    expect(obj1.b).toBe(3)
-    expect(dep1.notify.mock.calls.length).toBe(2)
-    // set non-existing key
-    setProp(obj1, 'c', 1)
-    expect(obj1.c).toBe(1)
-    expect(dep1.notify.mock.calls.length).toBe(3)
-    // should ignore deleting non-existing key
-    delProp(obj1, 'a')
-    expect(dep1.notify.mock.calls.length).toBe(3)
-    // should work on non-observed objects
-    const obj2 = { a: 1 }
-    delProp(obj2, 'a')
-    expect(hasOwn(obj2, 'a')).toBe(false)
-    // should work on Object.create(null)
-    const obj3: any = Object.create(null)
-    obj3.a = 1
-    const ob3 = observe(obj3) as any
-    const dep3 = ob3.dep
-    vi.spyOn(dep3, 'notify')
-    setProp(obj3, 'b', 2)
-    expect(obj3.b).toBe(2)
-    expect(dep3.notify.mock.calls.length).toBe(1)
-    delProp(obj3, 'a')
-    expect(hasOwn(obj3, 'a')).toBe(false)
-    expect(dep3.notify.mock.calls.length).toBe(2)
-    // set and delete non-numeric key on array
-    const arr2: any = ['a']
-    const ob2 = observe(arr2) as any
-    const dep2 = ob2.dep
-    vi.spyOn(dep2, 'notify')
-    setProp(arr2, 'b', 2)
-    expect(arr2.b).toBe(2)
-    expect(dep2.notify.mock.calls.length).toBe(1)
-    delProp(arr2, 'b')
-    expect(hasOwn(arr2, 'b')).toBe(false)
-    expect(dep2.notify.mock.calls.length).toBe(2)
-  })
-
-  it('warning set/delete on a Vue instance', done => {
-    const vm = new Vue({
-      template: '<div>{{a}}</div>',
-      data: { a: 1 }
-    }).$mount()
-    expect(vm.$el.outerHTML).toBe('<div>1</div>')
-    Vue.set(vm, 'a', 2)
-    waitForUpdate(() => {
-      expect(vm.$el.outerHTML).toBe('<div>2</div>')
-      expect(
-        'Avoid adding reactive properties to a Vue instance'
-      ).not.toHaveBeenWarned()
-      Vue.delete(vm, 'a')
-    })
-      .then(() => {
-        expect('Avoid deleting properties on a Vue instance').toHaveBeenWarned()
-        expect(vm.$el.outerHTML).toBe('<div>2</div>')
-        Vue.set(vm, 'b', 123)
-        expect(
-          'Avoid adding reactive properties to a Vue instance'
-        ).toHaveBeenWarned()
-      })
-      .then(done)
-  })
-
-  it('warning set/delete on Vue instance root $data', done => {
-    const data = { a: 1 }
-    const vm = new Vue({
-      template: '<div>{{a}}</div>',
-      data
-    }).$mount()
-    expect(vm.$el.outerHTML).toBe('<div>1</div>')
-    expect(Vue.set(data, 'a', 2)).toBe(2)
-    waitForUpdate(() => {
-      expect(vm.$el.outerHTML).toBe('<div>2</div>')
-      expect(
-        'Avoid adding reactive properties to a Vue instance'
-      ).not.toHaveBeenWarned()
-      Vue.delete(data, 'a')
-    })
-      .then(() => {
-        expect('Avoid deleting properties on a Vue instance').toHaveBeenWarned()
-        expect(vm.$el.outerHTML).toBe('<div>2</div>')
-        expect(Vue.set(data, 'b', 123)).toBe(123)
-        expect(
-          'Avoid adding reactive properties to a Vue instance'
-        ).toHaveBeenWarned()
-      })
-      .then(done)
-  })
-
-  it('observing array mutation', () => {
-    const arr: any[] = []
-    const ob = observe(arr) as any
-    const dep = ob.dep
-    vi.spyOn(dep, 'notify')
-    const objs = [{}, {}, {}]
-    arr.push(objs[0])
-    arr.pop()
-    arr.unshift(objs[1])
-    arr.shift()
-    arr.splice(0, 0, objs[2])
-    arr.sort()
-    arr.reverse()
-    expect(dep.notify.mock.calls.length).toBe(7)
-    // inserted elements should be observed
-    objs.forEach((obj: any) => {
-      expect(obj.__ob__ instanceof Observer).toBe(true)
-    })
-  })
-
-  it('warn set/delete on non valid values', () => {
-    try {
-      // @ts-expect-error
-      setProp(null, 'foo', 1)
-    } catch (e) {}
-    expect(
-      `Cannot set reactive property on undefined, null, or primitive value`
-    ).toHaveBeenWarned()
-
-    try {
-      // @ts-expect-error
-      delProp(null, 'foo')
-    } catch (e) {}
-    expect(
-      `Cannot delete reactive property on undefined, null, or primitive value`
-    ).toHaveBeenWarned()
-  })
-
-  it('should lazy invoke existing getters', () => {
-    const obj: any = {}
-    let called = false
-    Object.defineProperty(obj, 'getterProp', {
-      enumerable: true,
-      get: () => {
-        called = true
-        return 'some value'
-      }
-    })
-    observe(obj)!
-    expect(called).toBe(false)
-  })
-})
diff --git a/test/unit/modules/observer/scheduler.spec.ts b/test/unit/modules/observer/scheduler.spec.ts
deleted file mode 100644
index 001ee5a429a..00000000000
--- a/test/unit/modules/observer/scheduler.spec.ts
+++ /dev/null
@@ -1,184 +0,0 @@
-import Vue from 'vue'
-import {
-  MAX_UPDATE_COUNT,
-  queueWatcher as _queueWatcher
-} from 'core/observer/scheduler'
-
-function queueWatcher(watcher) {
-  watcher.vm = {} // mock vm
-  _queueWatcher(watcher)
-}
-
-describe('Scheduler', () => {
-  let spy
-  beforeEach(() => {
-    spy = vi.fn()
-  })
-
-  it('queueWatcher', done => {
-    queueWatcher({
-      run: spy
-    })
-    waitForUpdate(() => {
-      expect(spy.mock.calls.length).toBe(1)
-    }).then(done)
-  })
-
-  it('dedup', done => {
-    queueWatcher({
-      id: 1,
-      run: spy
-    })
-    queueWatcher({
-      id: 1,
-      run: spy
-    })
-    waitForUpdate(() => {
-      expect(spy.mock.calls.length).toBe(1)
-    }).then(done)
-  })
-
-  it('allow duplicate when flushing', done => {
-    const job = {
-      id: 1,
-      run: spy
-    }
-    queueWatcher(job)
-    queueWatcher({
-      id: 2,
-      run() {
-        queueWatcher(job)
-      }
-    })
-    waitForUpdate(() => {
-      expect(spy.mock.calls.length).toBe(2)
-    }).then(done)
-  })
-
-  it('call user watchers before component re-render', done => {
-    const calls: any[] = []
-    const vm = new Vue({
-      data: {
-        a: 1
-      },
-      template: '<div>{{ a }}</div>',
-      watch: {
-        a() {
-          calls.push(1)
-        }
-      },
-      beforeUpdate() {
-        calls.push(2)
-      }
-    }).$mount()
-    vm.a = 2
-    waitForUpdate(() => {
-      expect(calls).toEqual([1, 2])
-    }).then(done)
-  })
-
-  it('call user watcher triggered by component re-render immediately', done => {
-    // this happens when a component re-render updates the props of a child
-    const calls: any[] = []
-    const vm = new Vue({
-      data: {
-        a: 1
-      },
-      watch: {
-        a() {
-          calls.push(1)
-        }
-      },
-      beforeUpdate() {
-        calls.push(2)
-      },
-      template: '<div><test :a="a"></test></div>',
-      components: {
-        test: {
-          props: ['a'],
-          template: '<div>{{ a }}</div>',
-          watch: {
-            a() {
-              calls.push(3)
-            }
-          },
-          beforeUpdate() {
-            calls.push(4)
-          }
-        }
-      }
-    }).$mount()
-    vm.a = 2
-    waitForUpdate(() => {
-      expect(calls).toEqual([1, 2, 3, 4])
-    }).then(done)
-  })
-
-  it('warn against infinite update loops', function (done) {
-    let count = 0
-    const job = {
-      id: 1,
-      run() {
-        count++
-        queueWatcher(job)
-      }
-    }
-    queueWatcher(job)
-    waitForUpdate(() => {
-      expect(count).toBe(MAX_UPDATE_COUNT + 1)
-      expect('infinite update loop').toHaveBeenWarned()
-    }).then(done)
-  })
-
-  it('should call newly pushed watcher after current watcher is done', done => {
-    const callOrder: any[] = []
-    queueWatcher({
-      id: 1,
-      user: true,
-      run() {
-        callOrder.push(1)
-        queueWatcher({
-          id: 2,
-          run() {
-            callOrder.push(3)
-          }
-        })
-        callOrder.push(2)
-      }
-    })
-    waitForUpdate(() => {
-      expect(callOrder).toEqual([1, 2, 3])
-    }).then(done)
-  })
-
-  // GitHub issue #5191
-  it('emit should work when updated hook called', done => {
-    const el = document.createElement('div')
-    const vm = new Vue({
-      template: `<div><child @change="bar" :foo="foo"></child></div>`,
-      data: {
-        foo: 0
-      },
-      methods: {
-        bar: spy
-      },
-      components: {
-        child: {
-          template: `<div>{{foo}}</div>`,
-          props: ['foo'],
-          updated() {
-            this.$emit('change')
-          }
-        }
-      }
-    }).$mount(el)
-    vm.$nextTick(() => {
-      vm.foo = 1
-      vm.$nextTick(() => {
-        expect(vm.$el.innerHTML).toBe('<div>1</div>')
-        expect(spy).toHaveBeenCalled()
-        done()
-      })
-    })
-  })
-})
diff --git a/test/unit/modules/observer/watcher.spec.ts b/test/unit/modules/observer/watcher.spec.ts
deleted file mode 100644
index 1525d30b397..00000000000
--- a/test/unit/modules/observer/watcher.spec.ts
+++ /dev/null
@@ -1,203 +0,0 @@
-import Vue from 'vue'
-import Watcher from 'core/observer/watcher'
-
-describe('Watcher', () => {
-  let vm, spy
-  beforeEach(() => {
-    vm = new Vue({
-      template: '<div></div>',
-      data: {
-        a: 1,
-        b: {
-          c: 2,
-          d: 4
-        },
-        c: 'c',
-        msg: 'yo'
-      }
-    }).$mount()
-    spy = vi.fn()
-  })
-
-  it('path', done => {
-    const watcher = new Watcher(vm, 'b.c', spy)
-    expect(watcher.value).toBe(2)
-    vm.b.c = 3
-    waitForUpdate(() => {
-      expect(watcher.value).toBe(3)
-      expect(spy).toHaveBeenCalledWith(3, 2)
-      vm.b = { c: 4 } // swapping the object
-    })
-      .then(() => {
-        expect(watcher.value).toBe(4)
-        expect(spy).toHaveBeenCalledWith(4, 3)
-      })
-      .then(done)
-  })
-
-  it('non-existent path, set later', done => {
-    const watcher1 = new Watcher(vm, 'b.e', spy)
-    expect(watcher1.value).toBeUndefined()
-    // check $add should not affect isolated children
-    const child2 = new Vue({ parent: vm })
-    const watcher2 = new Watcher(child2, 'b.e', spy)
-    expect(watcher2.value).toBeUndefined()
-    Vue.set(vm.b, 'e', 123)
-    waitForUpdate(() => {
-      expect(watcher1.value).toBe(123)
-      expect(watcher2.value).toBeUndefined()
-      expect(spy.mock.calls.length).toBe(1)
-      expect(spy).toHaveBeenCalledWith(123, undefined)
-    }).then(done)
-  })
-
-  it('delete', done => {
-    const watcher = new Watcher(vm, 'b.c', spy)
-    expect(watcher.value).toBe(2)
-    Vue.delete(vm.b, 'c')
-    waitForUpdate(() => {
-      expect(watcher.value).toBeUndefined()
-      expect(spy).toHaveBeenCalledWith(undefined, 2)
-    }).then(done)
-  })
-
-  it('path containing $data', done => {
-    const watcher = new Watcher(vm, '$data.b.c', spy)
-    expect(watcher.value).toBe(2)
-    vm.b = { c: 3 }
-    waitForUpdate(() => {
-      expect(watcher.value).toBe(3)
-      expect(spy).toHaveBeenCalledWith(3, 2)
-      vm.$data.b.c = 4
-    })
-      .then(() => {
-        expect(watcher.value).toBe(4)
-        expect(spy).toHaveBeenCalledWith(4, 3)
-      })
-      .then(done)
-  })
-
-  it('deep watch', done => {
-    let oldB
-    new Watcher(vm, 'b', spy, {
-      deep: true
-    })
-    vm.b.c = { d: 4 }
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
-      oldB = vm.b
-      vm.b = { c: [{ a: 1 }] }
-    })
-      .then(() => {
-        expect(spy).toHaveBeenCalledWith(vm.b, oldB)
-        expect(spy.mock.calls.length).toBe(2)
-        vm.b.c[0].a = 2
-      })
-      .then(() => {
-        expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
-        expect(spy.mock.calls.length).toBe(3)
-      })
-      .then(done)
-  })
-
-  it('deep watch $data', done => {
-    new Watcher(vm, '$data', spy, {
-      deep: true
-    })
-    vm.b.c = 3
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(vm.$data, vm.$data)
-    }).then(done)
-  })
-
-  it('deep watch with circular references', done => {
-    new Watcher(vm, 'b', spy, {
-      deep: true
-    })
-    Vue.set(vm.b, '_', vm.b)
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
-      expect(spy.mock.calls.length).toBe(1)
-      vm.b._.c = 1
-    })
-      .then(() => {
-        expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
-        expect(spy.mock.calls.length).toBe(2)
-      })
-      .then(done)
-  })
-
-  it('fire change for prop addition/deletion in non-deep mode', done => {
-    new Watcher(vm, 'b', spy)
-    Vue.set(vm.b, 'e', 123)
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
-      expect(spy.mock.calls.length).toBe(1)
-      Vue.delete(vm.b, 'e')
-    })
-      .then(() => {
-        expect(spy.mock.calls.length).toBe(2)
-      })
-      .then(done)
-  })
-
-  it('watch function', done => {
-    const watcher = new Watcher(
-      vm,
-      function () {
-        return this.a + this.b.d
-      },
-      spy
-    )
-    expect(watcher.value).toBe(5)
-    vm.a = 2
-    waitForUpdate(() => {
-      expect(spy).toHaveBeenCalledWith(6, 5)
-      vm.b = { d: 2 }
-    })
-      .then(() => {
-        expect(spy).toHaveBeenCalledWith(4, 6)
-      })
-      .then(done)
-  })
-
-  it('lazy mode', done => {
-    const watcher = new Watcher(
-      vm,
-      function () {
-        return this.a + this.b.d
-      },
-      null,
-      { lazy: true }
-    )
-    expect(watcher.lazy).toBe(true)
-    expect(watcher.value).toBeUndefined()
-    expect(watcher.dirty).toBe(true)
-    watcher.evaluate()
-    expect(watcher.value).toBe(5)
-    expect(watcher.dirty).toBe(false)
-    vm.a = 2
-    waitForUpdate(() => {
-      expect(watcher.value).toBe(5)
-      expect(watcher.dirty).toBe(true)
-      watcher.evaluate()
-      expect(watcher.value).toBe(6)
-      expect(watcher.dirty).toBe(false)
-    }).then(done)
-  })
-
-  it('teardown', done => {
-    const watcher = new Watcher(vm, 'b.c', spy)
-    watcher.teardown()
-    vm.b.c = 3
-    waitForUpdate(() => {
-      expect(watcher.active).toBe(false)
-      expect(spy).not.toHaveBeenCalled()
-    }).then(done)
-  })
-
-  it('warn not support path', () => {
-    new Watcher(vm, 'd.e + c', spy)
-    expect('Failed watching path:').toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/modules/server-compiler/compiler-options.spec.ts b/test/unit/modules/server-compiler/compiler-options.spec.ts
deleted file mode 100644
index d6bc36393ff..00000000000
--- a/test/unit/modules/server-compiler/compiler-options.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { ssrCompile } from 'server/compiler'
-
-describe('ssrCompile options', () => {
-  it('comments', () => {
-    const compiled = ssrCompile(
-      `
-      <div>
-        <!-- test comments -->
-      </div>
-    `,
-      { comments: true }
-    )
-
-    expect(compiled.render).toContain('<!-- test comments -->')
-  })
-})
diff --git a/test/unit/modules/util/error.spec.ts b/test/unit/modules/util/error.spec.ts
deleted file mode 100644
index 1ed8b72107c..00000000000
--- a/test/unit/modules/util/error.spec.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import Vue from 'vue'
-import { invokeWithErrorHandling } from 'core/util/error'
-
-describe('invokeWithErrorHandling', () => {
-  if (typeof Promise !== 'undefined') {
-    it('should errorHandler call once when nested calls return rejected promise', done => {
-      const originalHandler = Vue.config.errorHandler
-      const handler = (Vue.config.errorHandler = vi.fn())
-      const userCatch = vi.fn()
-      const err = new Error('fake error')
-
-      invokeWithErrorHandling(() => {
-        return invokeWithErrorHandling(() => {
-          return Promise.reject(err)
-        })
-      })
-        .catch(userCatch)
-        .then(() => {
-          Vue.config.errorHandler = originalHandler
-          expect(handler.mock.calls.length).toBe(1)
-          expect(userCatch).toHaveBeenCalledWith(err)
-          done()
-        })
-    })
-  }
-})
diff --git a/test/unit/modules/util/next-tick.spec.ts b/test/unit/modules/util/next-tick.spec.ts
deleted file mode 100644
index 353848b488f..00000000000
--- a/test/unit/modules/util/next-tick.spec.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { nextTick } from 'core/util/next-tick'
-
-describe('nextTick', () => {
-  it('accepts a callback', done => {
-    nextTick(done)
-  })
-
-  it('returns undefined when passed a callback', () => {
-    expect(nextTick(() => {})).toBeUndefined()
-  })
-
-  if (typeof Promise !== 'undefined') {
-    it('returns a Promise when provided no callback', done => {
-      nextTick().then(done)
-    })
-
-    it('returns a Promise with a context argument when provided a falsy callback and an object', done => {
-      const obj = {}
-      nextTick(undefined, obj).then(ctx => {
-        expect(ctx).toBe(obj)
-        done()
-      })
-    })
-
-    it('returned Promise should resolve correctly vs callback', done => {
-      const spy = vi.fn()
-      nextTick(spy)
-      nextTick().then(() => {
-        expect(spy).toHaveBeenCalled()
-        done()
-      })
-    })
-  }
-})
diff --git a/test/unit/modules/util/toString.spec.ts b/test/unit/modules/util/toString.spec.ts
deleted file mode 100644
index 1d7e2a919ab..00000000000
--- a/test/unit/modules/util/toString.spec.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { toString } from 'core/util/index'
-import { ref } from 'v3'
-
-test('should unwrap refs', () => {
-  expect(
-    toString({
-      a: ref(0),
-      b: { c: ref(1) }
-    })
-  ).toBe(JSON.stringify({ a: 0, b: { c: 1 } }, null, 2))
-})
diff --git a/test/unit/modules/vdom/create-component.spec.ts b/test/unit/modules/vdom/create-component.spec.ts
deleted file mode 100644
index 55a9e085a46..00000000000
--- a/test/unit/modules/vdom/create-component.spec.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import Vue from 'vue'
-import { createComponent } from 'core/vdom/create-component'
-import { setCurrentRenderingInstance } from 'core/instance/render'
-
-describe('create-component', () => {
-  let vm
-  beforeEach(() => {
-    vm = new Vue({
-      template: '<p>{{msg}}</p>',
-      data() {
-        return { msg: 'hello, my children' }
-      }
-    }).$mount()
-    return new Promise(r => Vue.nextTick(r))
-  })
-
-  it('create a component basically', () => {
-    const child = {
-      name: 'child',
-      props: ['msg'],
-      render() {}
-    }
-    const data = {
-      props: { msg: 'hello world' },
-      attrs: { id: 1 },
-      staticAttrs: { class: 'foo' },
-      on: { notify: 'onNotify' }
-    }
-    const vnode = createComponent(child, data, vm, vm)
-    expect(vnode.tag).toMatch(/vue-component-[0-9]+-child/)
-    expect(vnode.data.attrs).toEqual({ id: 1 })
-    expect(vnode.data.staticAttrs).toEqual({ class: 'foo' })
-    expect(vnode.componentOptions.propsData).toEqual({ msg: 'hello world' })
-    expect(vnode.componentOptions.listeners).toEqual({ notify: 'onNotify' })
-    expect(vnode.children).toBeUndefined()
-    expect(vnode.text).toBeUndefined()
-    expect(vnode.elm).toBeUndefined()
-    expect(vnode.ns).toBeUndefined()
-    expect(vnode.context).toEqual(vm)
-  })
-
-  it('create a component when resolved with async loading', done => {
-    let vnode = null
-    const data = {
-      props: {},
-      staticAttrs: { class: 'foo' }
-    }
-    vi.spyOn(vm, '$forceUpdate')
-    function async(resolve, reject) {
-      setTimeout(() => {
-        resolve({
-          name: 'child',
-          props: ['msg']
-        })
-        Vue.nextTick(loaded)
-      }, 0)
-    }
-    function go() {
-      setCurrentRenderingInstance(vm)
-      vnode = createComponent(async, data, vm, vm)
-      setCurrentRenderingInstance(null)
-      expect(vnode.isComment).toBe(true) // not to be loaded yet.
-      expect(vnode.asyncFactory).toBe(async)
-      expect(vnode.asyncFactory.owners.length).toEqual(1)
-    }
-    function loaded() {
-      setCurrentRenderingInstance(vm)
-      vnode = createComponent(async, data, vm, vm)
-      setCurrentRenderingInstance(null)
-      expect(vnode.tag).toMatch(/vue-component-[0-9]+-child/)
-      expect(vnode.data.staticAttrs).toEqual({ class: 'foo' })
-      expect(vnode.children).toBeUndefined()
-      expect(vnode.text).toBeUndefined()
-      expect(vnode.elm).toBeUndefined()
-      expect(vnode.ns).toBeUndefined()
-      expect(vnode.context).toEqual(vm)
-      expect(vnode.asyncFactory.owners.length).toEqual(0)
-      expect(vm.$forceUpdate).toHaveBeenCalled()
-      done()
-    }
-    go()
-  })
-
-  it('create a component when resolved with synchronous async loading', done => {
-    const data = {
-      props: {},
-      staticAttrs: { class: 'bar' }
-    }
-    vi.spyOn(vm, '$forceUpdate')
-    function async(resolve, reject) {
-      resolve({
-        name: 'child',
-        props: ['msg']
-      })
-    }
-    setCurrentRenderingInstance(vm)
-    const vnode = createComponent(async, data, vm, vm)
-    setCurrentRenderingInstance(null)
-    expect(vnode.asyncFactory).toBe(async)
-    expect(vnode.asyncFactory.owners.length).toEqual(0)
-    expect(vnode.tag).toMatch(/vue-component-[0-9]+-child/)
-    expect(vnode.data.staticAttrs).toEqual({ class: 'bar' })
-    expect(vnode.children).toBeUndefined()
-    expect(vnode.text).toBeUndefined()
-    expect(vnode.elm).toBeUndefined()
-    expect(vnode.ns).toBeUndefined()
-    expect(vnode.context).toEqual(vm)
-    expect(vm.$forceUpdate).not.toHaveBeenCalled()
-    done()
-  })
-
-  it('not create a component when rejected with async loading', done => {
-    let vnode = null
-    const data = {
-      props: { msg: 'hello world' },
-      attrs: { id: 1 }
-    }
-    const reason = 'failed!!'
-    function async(resolve, reject) {
-      setTimeout(() => {
-        reject(reason)
-        Vue.nextTick(failed)
-      }, 0)
-    }
-    function go() {
-      setCurrentRenderingInstance(vm)
-      vnode = createComponent(async, data, vm, vm)
-      setCurrentRenderingInstance(null)
-      expect(vnode.isComment).toBe(true) // not to be loaded yet.
-    }
-    function failed() {
-      setCurrentRenderingInstance(vm)
-      vnode = createComponent(async, data, vm, vm)
-      setCurrentRenderingInstance(null)
-      expect(vnode.isComment).toBe(true) // failed, still a comment node
-      expect(
-        `Failed to resolve async component: ${async}\nReason: ${reason}`
-      ).toHaveBeenWarned()
-      done()
-    }
-    go()
-  })
-
-  it('not create a component when specified with falsy', () => {
-    const vnode = createComponent(null, {}, vm, vm)
-    expect(vnode).toBeUndefined()
-  })
-
-  it('warn component definition type', () => {
-    const Ctor = 'child'
-    const vnode = createComponent(Ctor, {}, vm, vm)
-    expect(vnode).toBeUndefined()
-    expect(`Invalid Component definition: ${Ctor}`).toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/modules/vdom/create-element.spec.ts b/test/unit/modules/vdom/create-element.spec.ts
deleted file mode 100644
index f97b4bc07cf..00000000000
--- a/test/unit/modules/vdom/create-element.spec.ts
+++ /dev/null
@@ -1,284 +0,0 @@
-import Vue from 'vue'
-import { createEmptyVNode } from 'core/vdom/vnode'
-
-describe('create-element', () => {
-  it('render vnode with basic reserved tag using createElement', () => {
-    const vm = new Vue({
-      data: { msg: 'hello world' }
-    })
-    const h = vm.$createElement
-    const vnode = h('p', {})
-    expect(vnode.tag).toBe('p')
-    expect(vnode.data).toEqual({})
-    expect(vnode.children).toBeUndefined()
-    expect(vnode.text).toBeUndefined()
-    expect(vnode.elm).toBeUndefined()
-    expect(vnode.ns).toBeUndefined()
-    expect(vnode.context).toEqual(vm)
-  })
-
-  it('render vnode with component using createElement', () => {
-    const vm = new Vue({
-      data: { message: 'hello world' },
-      components: {
-        'my-component': {
-          props: ['msg']
-        }
-      }
-    })
-    const h = vm.$createElement
-    const vnode = h('my-component', { props: { msg: vm.message } })
-    expect(vnode.tag).toMatch(/vue-component-[0-9]+/)
-    expect(vnode.componentOptions.propsData).toEqual({ msg: vm.message })
-    expect(vnode.children).toBeUndefined()
-    expect(vnode.text).toBeUndefined()
-    expect(vnode.elm).toBeUndefined()
-    expect(vnode.ns).toBeUndefined()
-    expect(vnode.context).toEqual(vm)
-  })
-
-  it('render vnode with custom tag using createElement', () => {
-    const vm = new Vue({
-      data: { msg: 'hello world' }
-    })
-    const h = vm.$createElement
-    const tag = 'custom-tag'
-    const vnode = h(tag, {})
-    expect(vnode.tag).toBe('custom-tag')
-    expect(vnode.data).toEqual({})
-    expect(vnode.children).toBeUndefined()
-    expect(vnode.text).toBeUndefined()
-    expect(vnode.elm).toBeUndefined()
-    expect(vnode.ns).toBeUndefined()
-    expect(vnode.context).toEqual(vm)
-    expect(vnode.componentOptions).toBeUndefined()
-  })
-
-  it('render empty vnode with falsy tag using createElement', () => {
-    const vm = new Vue({
-      data: { msg: 'hello world' }
-    })
-    const h = vm.$createElement
-    const vnode = h(null, {})
-    expect(vnode).toEqual(createEmptyVNode())
-  })
-
-  it('render vnode with not string tag using createElement', () => {
-    const vm = new Vue({
-      data: { msg: 'hello world' }
-    })
-    const h = vm.$createElement
-    const vnode = h(
-      Vue.extend({
-        // Component class
-        props: ['msg']
-      }),
-      { props: { msg: vm.message } }
-    )
-    expect(vnode.tag).toMatch(/vue-component-[0-9]+/)
-    expect(vnode.componentOptions.propsData).toEqual({ msg: vm.message })
-    expect(vnode.children).toBeUndefined()
-    expect(vnode.text).toBeUndefined()
-    expect(vnode.elm).toBeUndefined()
-    expect(vnode.ns).toBeUndefined()
-    expect(vnode.context).toEqual(vm)
-  })
-
-  it('render vnode with createElement with children', () => {
-    const vm = new Vue({})
-    const h = vm.$createElement
-    const vnode = h('p', void 0, [h('br'), 'hello world', h('br')])
-    expect(vnode.children[0].tag).toBe('br')
-    expect(vnode.children[1].text).toBe('hello world')
-    expect(vnode.children[2].tag).toBe('br')
-  })
-
-  it('render vnode with children, omitting data', () => {
-    const vm = new Vue({})
-    const h = vm.$createElement
-    const vnode = h('p', [h('br'), 'hello world', h('br')])
-    expect(vnode.children[0].tag).toBe('br')
-    expect(vnode.children[1].text).toBe('hello world')
-    expect(vnode.children[2].tag).toBe('br')
-  })
-
-  it('render vnode with children, including boolean and null type', () => {
-    const vm = new Vue({})
-    const h = vm.$createElement
-    const vnode = h('p', [h('br'), true, 123, h('br'), 'abc', null])
-    expect(vnode.children.length).toBe(4)
-    expect(vnode.children[0].tag).toBe('br')
-    expect(vnode.children[1].text).toBe('123')
-    expect(vnode.children[2].tag).toBe('br')
-    expect(vnode.children[3].text).toBe('abc')
-  })
-
-  it('render svg elements with correct namespace', () => {
-    const vm = new Vue({})
-    const h = vm.$createElement
-    const vnode = h('svg', [h('a', [h('foo', [h('bar')])])])
-    expect(vnode.ns).toBe('svg')
-    // should apply ns to children recursively
-    expect(vnode.children[0].ns).toBe('svg')
-    expect(vnode.children[0].children[0].ns).toBe('svg')
-    expect(vnode.children[0].children[0].children[0].ns).toBe('svg')
-  })
-
-  it('render MathML elements with correct namespace', () => {
-    const vm = new Vue({})
-    const h = vm.$createElement
-    const vnode = h('math', [h('matrix')])
-    expect(vnode.ns).toBe('math')
-    // should apply ns to children
-    expect(vnode.children[0].ns).toBe('math')
-    // although not explicitly listed, elements nested under <math>
-    // should not be treated as component
-    expect(vnode.children[0].componentOptions).toBeUndefined()
-  })
-
-  it('render svg foreignObject with correct namespace', () => {
-    const vm = new Vue({})
-    const h = vm.$createElement
-    const vnode = h('svg', [h('foreignObject', [h('p'), h('svg')])])
-    expect(vnode.ns).toBe('svg')
-    expect(vnode.children[0].ns).toBe('svg')
-    expect(vnode.children[0].children[0].ns).toBeUndefined()
-    // #7330
-    expect(vnode.children[0].children[1].ns).toBe('svg')
-  })
-
-  // #6642
-  it('render svg foreignObject component with correct namespace', () => {
-    const vm = new Vue({
-      template: `
-        <svg>
-          <test></test>
-        </svg>
-      `,
-      components: {
-        test: {
-          template: `
-          <foreignObject>
-            <p xmlns="http://www.w3.org/1999/xhtml"></p>
-          </foreignObject>
-          `
-        }
-      }
-    }).$mount()
-    const testComp = vm.$children[0]
-    expect(testComp.$vnode.ns).toBe('svg')
-    expect(testComp._vnode.tag).toBe('foreignObject')
-    expect(testComp._vnode.ns).toBe('svg')
-    expect(testComp._vnode.children[0].tag).toBe('p')
-    expect(testComp._vnode.children[0].ns).toBeUndefined()
-  })
-
-  // #6506
-  it('render SVGAElement in a component correctly', () => {
-    const vm = new Vue({
-      template: `
-        <svg>
-          <test></test>
-        </svg>
-      `,
-      components: {
-        test: { render: h => h('a') }
-      }
-    }).$mount()
-    const testComp = vm.$children[0]
-    expect(testComp.$vnode.ns).toBe('svg')
-    expect(testComp._vnode.tag).toBe('a')
-    expect(testComp._vnode.ns).toBe('svg')
-  })
-
-  it('warn observed data objects', () => {
-    new Vue({
-      data: {
-        data: {}
-      },
-      render(h) {
-        return h('div', this.data)
-      }
-    }).$mount()
-    expect('Avoid using observed data object as vnode data').toHaveBeenWarned()
-  })
-
-  it('warn non-primitive key', () => {
-    new Vue({
-      render(h) {
-        return h('div', { key: {} })
-      }
-    }).$mount()
-    expect('Avoid using non-primitive value as key').toHaveBeenWarned()
-  })
-
-  it("doesn't warn boolean key", () => {
-    new Vue({
-      render(h) {
-        return h('div', { key: true })
-      }
-    }).$mount()
-    expect('Avoid using non-primitive value as key').not.toHaveBeenWarned()
-  })
-
-  it("doesn't warn symbol key", () => {
-    new Vue({
-      render(h) {
-        return h('div', { key: Symbol('symbol') })
-      }
-    }).$mount()
-    expect('Avoid using non-primitive value as key').not.toHaveBeenWarned()
-  })
-
-  it('nested child elements should be updated correctly', done => {
-    const vm = new Vue({
-      data: { n: 1 },
-      render(h) {
-        const list: any[] = []
-        for (let i = 0; i < this.n; i++) {
-          list.push(h('span', i))
-        }
-        const input = h('input', {
-          attrs: {
-            value: 'a',
-            type: 'text'
-          }
-        })
-        return h('div', [[...list, input]])
-      }
-    }).$mount()
-    expect(vm.$el.innerHTML).toContain('<span>0</span><input')
-    const el = vm.$el.querySelector('input')
-    el.value = 'b'
-    vm.n++
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toContain('<span>0</span><span>1</span><input')
-      expect(vm.$el.querySelector('input')).toBe(el)
-      expect(vm.$el.querySelector('input').value).toBe('b')
-    }).then(done)
-  })
-
-  // #7786
-  it('creates element with vnode reference in :class or :style', () => {
-    const vm = new Vue({
-      components: {
-        foo: {
-          render(h) {
-            return h(
-              'div',
-              {
-                class: {
-                  'has-vnode': this.$vnode
-                }
-              },
-              'foo'
-            )
-          }
-        }
-      },
-      render: h => h('foo')
-    }).$mount()
-    expect(vm.$el.innerHTML).toContain('foo')
-    expect(vm.$el.classList.contains('has-vnode')).toBe(true)
-  })
-})
diff --git a/test/unit/modules/vdom/modules/attrs.spec.ts b/test/unit/modules/vdom/modules/attrs.spec.ts
deleted file mode 100644
index fa654a65292..00000000000
--- a/test/unit/modules/vdom/modules/attrs.spec.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-import Vue from 'vue'
-import { patch } from 'web/runtime/patch'
-import VNode from 'core/vdom/vnode'
-import { xlinkNS } from 'web/util/index'
-
-describe('vdom attrs module', () => {
-  it('should create an element with attrs', () => {
-    const vnode = new VNode('p', { attrs: { id: 1, class: 'class1' } })
-    const elm = patch(null, vnode)
-    expect(elm.id).toBe('1')
-    expect(elm).toHaveClass('class1')
-  })
-
-  it('should change the elements attrs', () => {
-    const vnode1 = new VNode('i', { attrs: { id: '1', class: 'i am vdom' } })
-    const vnode2 = new VNode('i', { attrs: { id: '2', class: 'i am' } })
-    patch(null, vnode1)
-    const elm = patch(vnode1, vnode2)
-    expect(elm.id).toBe('2')
-    expect(elm).toHaveClass('i')
-    expect(elm).toHaveClass('am')
-    expect(elm).not.toHaveClass('vdom')
-  })
-
-  it('should remove the elements attrs', () => {
-    const vnode1 = new VNode('i', { attrs: { id: '1', class: 'i am vdom' } })
-    const vnode2 = new VNode('i', { attrs: { id: '1' } })
-    patch(null, vnode1)
-    const elm = patch(vnode1, vnode2)
-    expect(elm.id).toBe('1')
-    expect(elm.className).toBe('')
-  })
-
-  it('should remove the elements attrs for new nodes without attrs data', () => {
-    const vnode1 = new VNode('i', { attrs: { id: '1', class: 'i am vdom' } })
-    const vnode2 = new VNode('i', {})
-    patch(null, vnode1)
-    const elm = patch(vnode1, vnode2)
-    expect(elm.id).toBe('')
-    expect(elm.className).toBe('')
-  })
-
-  it('should remove the falsy value from boolean attr', () => {
-    const vnode = new VNode('option', { attrs: { disabled: null } })
-    const elm = patch(null, vnode)
-    expect(elm.getAttribute('disabled')).toBe(null)
-  })
-
-  it('should set the attr name to boolean attr', () => {
-    const vnode = new VNode('option', { attrs: { disabled: true } })
-    const elm = patch(null, vnode)
-    expect(elm.getAttribute('disabled')).toBe('disabled')
-  })
-
-  it('should set the falsy value to enumerated attr', () => {
-    const vnode = new VNode('div', { attrs: { contenteditable: null } })
-    const elm = patch(null, vnode)
-    expect(elm.getAttribute('contenteditable')).toBe('false')
-  })
-
-  it('should set the boolean string value to enumerated attr', () => {
-    const vnode = new VNode('div', { attrs: { contenteditable: 'true' } })
-    const elm = patch(null, vnode)
-    expect(elm.getAttribute('contenteditable')).toBe('true')
-  })
-
-  it('should set the xlink value to attr', () => {
-    const vnode = new VNode('a', { attrs: { 'xlink:href': '#id1' } })
-    const elm = patch(null, vnode)
-    expect(elm.getAttributeNS(xlinkNS, 'href')).toBe('#id1')
-  })
-
-  it('should set the xlink boolean string value to attr', () => {
-    const vnode = new VNode('option', { attrs: { 'xlink:disabled': true } })
-    const elm = patch(null, vnode)
-    expect(elm.getAttributeNS(xlinkNS, 'disabled')).toBe('true')
-  })
-
-  it('should handle mutating observed attrs object', done => {
-    const vm = new Vue({
-      data: {
-        attrs: {
-          id: 'foo'
-        }
-      },
-      render(h) {
-        return h('div', {
-          attrs: this.attrs
-        })
-      }
-    }).$mount()
-
-    expect(vm.$el.id).toBe('foo')
-    vm.attrs.id = 'bar'
-    waitForUpdate(() => {
-      expect(vm.$el.id).toBe('bar')
-      vm.attrs = { id: 'baz' }
-    })
-      .then(() => {
-        expect(vm.$el.id).toBe('baz')
-      })
-      .then(done)
-  })
-})
diff --git a/test/unit/modules/vdom/modules/class.spec.ts b/test/unit/modules/vdom/modules/class.spec.ts
deleted file mode 100644
index 1d0dee4d232..00000000000
--- a/test/unit/modules/vdom/modules/class.spec.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-import { patch } from 'web/runtime/patch'
-import VNode from 'core/vdom/vnode'
-
-describe('vdom class module', () => {
-  it('should create an element with staticClass', () => {
-    const vnode = new VNode('p', { staticClass: 'class1' })
-    const elm = patch(null, vnode)
-    expect(elm).toHaveClass('class1')
-  })
-
-  it('should create an element with class', () => {
-    const vnode = new VNode('p', { class: 'class1' })
-    const elm = patch(null, vnode)
-    expect(elm).toHaveClass('class1')
-  })
-
-  it('should create an element with array class', () => {
-    const vnode = new VNode('p', { class: ['class1', 'class2'] })
-    const elm = patch(null, vnode)
-    expect(elm).toHaveClass('class1')
-    expect(elm).toHaveClass('class2')
-  })
-
-  it('should create an element with object class', () => {
-    const vnode = new VNode('p', {
-      class: { class1: true, class2: false, class3: true }
-    })
-    const elm = patch(null, vnode)
-    expect(elm).toHaveClass('class1')
-    expect(elm).not.toHaveClass('class2')
-    expect(elm).toHaveClass('class3')
-  })
-
-  it('should create an element with mixed class', () => {
-    const vnode = new VNode('p', {
-      class: [
-        { class1: false, class2: true, class3: false },
-        'class4',
-        ['class5', 'class6']
-      ]
-    })
-    const elm = patch(null, vnode)
-    expect(elm).not.toHaveClass('class1')
-    expect(elm).toHaveClass('class2')
-    expect(elm).not.toHaveClass('class3')
-    expect(elm).toHaveClass('class4')
-    expect(elm).toHaveClass('class5')
-    expect(elm).toHaveClass('class6')
-  })
-
-  it('should create an element with staticClass and class', () => {
-    const vnode = new VNode('p', { staticClass: 'class1', class: 'class2' })
-    const elm = patch(null, vnode)
-    expect(elm).toHaveClass('class1')
-    expect(elm).toHaveClass('class2')
-  })
-
-  it('should handle transition class', () => {
-    const vnode1 = new VNode('p', {
-      class: { class1: true, class2: false, class3: true }
-    })
-    let elm = patch(null, vnode1)
-    elm._transitionClasses = ['class4']
-    const vnode2 = new VNode('p', {
-      class: { class1: true, class2: true, class3: true }
-    })
-    elm = patch(vnode1, vnode2)
-    expect(elm).toHaveClass('class1')
-    expect(elm).toHaveClass('class2')
-    expect(elm).toHaveClass('class3')
-    expect(elm).toHaveClass('class4')
-  })
-
-  it('should change the elements class', () => {
-    const vnode1 = new VNode('p', {
-      class: { class1: true, class2: false, class3: true }
-    })
-    const vnode2 = new VNode('p', { staticClass: 'foo bar' })
-    let elm = patch(null, vnode1)
-    elm = patch(vnode1, vnode2)
-    expect(elm).not.toHaveClass('class1')
-    expect(elm).not.toHaveClass('class2')
-    expect(elm).not.toHaveClass('class3')
-    expect(elm).toHaveClass('foo')
-    expect(elm).toHaveClass('bar')
-  })
-
-  it('should remove the elements class', () => {
-    const vnode1 = new VNode('p', {
-      class: { class1: true, class2: false, class3: true }
-    })
-    const vnode2 = new VNode('p', { class: {} })
-    let elm = patch(null, vnode1)
-    elm = patch(vnode1, vnode2)
-    expect(elm).not.toHaveClass('class1')
-    expect(elm).not.toHaveClass('class2')
-    expect(elm).not.toHaveClass('class3')
-  })
-
-  it('should remove class for new nodes without class data', () => {
-    const vnode1 = new VNode('p', {
-      class: { class1: true, class2: false, class3: true }
-    })
-    const vnode2 = new VNode('p', {})
-    let elm = patch(null, vnode1)
-    elm = patch(vnode1, vnode2)
-    expect(elm).not.toHaveClass('class1')
-    expect(elm).not.toHaveClass('class2')
-    expect(elm).not.toHaveClass('class3')
-  })
-})
diff --git a/test/unit/modules/vdom/modules/directive.spec.ts b/test/unit/modules/vdom/modules/directive.spec.ts
deleted file mode 100644
index d17d66ba6db..00000000000
--- a/test/unit/modules/vdom/modules/directive.spec.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import Vue from 'vue'
-import { patch } from 'web/runtime/patch'
-import VNode from 'core/vdom/vnode'
-
-describe('vdom directive module', () => {
-  it('should work', () => {
-    const directive1 = {
-      bind: vi.fn(),
-      update: vi.fn(),
-      unbind: vi.fn()
-    }
-    const vm = new Vue({ directives: { directive1 } })
-    // create
-    const vnode1 = new VNode('div', {}, [
-      new VNode(
-        'p',
-        {
-          directives: [
-            {
-              name: 'directive1',
-              value: 'hello',
-              arg: 'arg1',
-              modifiers: { modifier1: true }
-            }
-          ]
-        },
-        undefined,
-        'hello world',
-        undefined,
-        vm
-      )
-    ])
-    patch(null, vnode1)
-    expect(directive1.bind).toHaveBeenCalled()
-    // update
-    const vnode2 = new VNode('div', {}, [
-      new VNode(
-        'p',
-        {
-          directives: [
-            {
-              name: 'directive1',
-              value: 'world',
-              arg: 'arg1',
-              modifiers: { modifier1: true }
-            }
-          ]
-        },
-        undefined,
-        'hello world',
-        undefined,
-        vm
-      )
-    ])
-    patch(vnode1, vnode2)
-    expect(directive1.update).toHaveBeenCalled()
-    // destroy
-    const vnode3 = new VNode('div')
-    patch(vnode2, vnode3)
-    expect(directive1.unbind).toHaveBeenCalled()
-  })
-})
diff --git a/test/unit/modules/vdom/modules/dom-props.spec.ts b/test/unit/modules/vdom/modules/dom-props.spec.ts
deleted file mode 100644
index 0f5d3aa0313..00000000000
--- a/test/unit/modules/vdom/modules/dom-props.spec.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-import Vue from 'vue'
-import { patch } from 'web/runtime/patch'
-import VNode from 'core/vdom/vnode'
-
-describe('vdom domProps module', () => {
-  it('should create an element with domProps', () => {
-    const vnode = new VNode('a', { domProps: { src: 'http://localhost/' } })
-    const elm = patch(null, vnode)
-    expect(elm.src).toBe('http://localhost/')
-  })
-
-  it('should change the elements domProps', () => {
-    const vnode1 = new VNode('a', { domProps: { src: 'http://localhost/' } })
-    const vnode2 = new VNode('a', { domProps: { src: 'https://vuejs.org/' } })
-    patch(null, vnode1)
-    const elm = patch(vnode1, vnode2)
-    expect(elm.src).toBe('https://vuejs.org/')
-  })
-
-  it('should remove the elements domProps', () => {
-    const vnode1 = new VNode('a', { domProps: { src: 'http://localhost/' } })
-    const vnode2 = new VNode('a', { domProps: {} })
-    patch(null, vnode1)
-    const elm = patch(vnode1, vnode2)
-    expect(elm.src).toBe('')
-  })
-
-  it('should initialize the elements value to zero', () => {
-    const vnode = new VNode('input', { domProps: { value: 0 } })
-    const elm = patch(null, vnode)
-    expect(elm.value).toBe('0')
-  })
-
-  it('should save raw value on element', () => {
-    const value = {}
-    const vnode = new VNode('input', { domProps: { value } })
-    const elm = patch(null, vnode)
-    expect(elm._value).toBe(value)
-  })
-
-  it('should discard vnode children if the node has innerHTML or textContent as a prop', () => {
-    const vnode = new VNode('div', { domProps: { innerHTML: 'hi' } }, [
-      new VNode('span'),
-      new VNode('span')
-    ])
-    const elm = patch(null, vnode)
-    expect(elm.innerHTML).toBe('hi')
-    expect(vnode.children.length).toBe(0)
-
-    const vnode2 = new VNode('div', { domProps: { textContent: 'hi' } }, [
-      new VNode('span'),
-      new VNode('span')
-    ])
-    const elm2 = patch(null, vnode2)
-    expect(elm2.textContent).toBe('hi')
-    expect(vnode2.children.length).toBe(0)
-
-    const vnode3 = new VNode('div', undefined, undefined, '123')
-    patch(null, vnode3)
-    const elm3 = patch(vnode3, vnode2)
-    expect(elm3.textContent).toBe('hi')
-
-    const vnode4 = new VNode('div', undefined, undefined, new VNode('span'))
-    patch(null, vnode4)
-    const elm4 = patch(vnode4, vnode)
-    expect(elm4.textContent).toBe('hi')
-  })
-
-  it('should handle mutating observed props object', done => {
-    const vm = new Vue({
-      data: {
-        props: {
-          id: 'foo'
-        }
-      },
-      render(h) {
-        return h('div', {
-          domProps: this.props
-        })
-      }
-    }).$mount()
-
-    expect(vm.$el.id).toBe('foo')
-    vm.props.id = 'bar'
-    waitForUpdate(() => {
-      expect(vm.$el.id).toBe('bar')
-      vm.props = { id: 'baz' }
-    })
-      .then(() => {
-        expect(vm.$el.id).toBe('baz')
-      })
-      .then(done)
-  })
-})
diff --git a/test/unit/modules/vdom/modules/events.spec.ts b/test/unit/modules/vdom/modules/events.spec.ts
deleted file mode 100644
index 514054783aa..00000000000
--- a/test/unit/modules/vdom/modules/events.spec.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-import { patch } from 'web/runtime/patch'
-import VNode from 'core/vdom/vnode'
-
-describe('vdom events module', () => {
-  it('should attach event handler to element', () => {
-    const click = vi.fn()
-    const vnode = new VNode('a', { on: { click } })
-
-    const elm = patch(null, vnode)
-    document.body.appendChild(elm)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(1)
-  })
-
-  it('should not duplicate the same listener', () => {
-    const click = vi.fn()
-    const vnode1 = new VNode('a', { on: { click } })
-    const vnode2 = new VNode('a', { on: { click } })
-
-    const elm = patch(null, vnode1)
-    patch(vnode1, vnode2)
-    document.body.appendChild(elm)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(1)
-  })
-
-  it('should update different listener', () => {
-    const click = vi.fn()
-    const click2 = vi.fn()
-    const vnode1 = new VNode('a', { on: { click } })
-    const vnode2 = new VNode('a', { on: { click: click2 } })
-
-    const elm = patch(null, vnode1)
-    document.body.appendChild(elm)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(1)
-    expect(click2.mock.calls.length).toBe(0)
-
-    patch(vnode1, vnode2)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(1)
-    expect(click2.mock.calls.length).toBe(1)
-  })
-
-  it('should attach Array of multiple handlers', () => {
-    const click = vi.fn()
-    const vnode = new VNode('a', { on: { click: [click, click] } })
-
-    const elm = patch(null, vnode)
-    document.body.appendChild(elm)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(2)
-  })
-
-  it('should update Array of multiple handlers', () => {
-    const click = vi.fn()
-    const click2 = vi.fn()
-    const vnode1 = new VNode('a', { on: { click: [click, click2] } })
-    const vnode2 = new VNode('a', { on: { click: [click] } })
-
-    const elm = patch(null, vnode1)
-    document.body.appendChild(elm)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(1)
-    expect(click2.mock.calls.length).toBe(1)
-
-    patch(vnode1, vnode2)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(2)
-    expect(click2.mock.calls.length).toBe(1)
-  })
-
-  it('should remove handlers that are no longer present', () => {
-    const click = vi.fn()
-    const vnode1 = new VNode('a', { on: { click } })
-    const vnode2 = new VNode('a', {})
-
-    const elm = patch(null, vnode1)
-    document.body.appendChild(elm)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(1)
-
-    patch(vnode1, vnode2)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(1)
-  })
-
-  it('should remove Array handlers that are no longer present', () => {
-    const click = vi.fn()
-    const vnode1 = new VNode('a', { on: { click: [click, click] } })
-    const vnode2 = new VNode('a', {})
-
-    const elm = patch(null, vnode1)
-    document.body.appendChild(elm)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(2)
-
-    patch(vnode1, vnode2)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(2)
-  })
-
-  // #4650
-  it('should handle single -> array or array -> single handler changes', () => {
-    const click = vi.fn()
-    const click2 = vi.fn()
-    const click3 = vi.fn()
-    const vnode0 = new VNode('a', { on: { click: click } })
-    const vnode1 = new VNode('a', { on: { click: [click, click2] } })
-    const vnode2 = new VNode('a', { on: { click: click } })
-    const vnode3 = new VNode('a', { on: { click: [click2, click3] } })
-
-    const elm = patch(null, vnode0)
-    document.body.appendChild(elm)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(1)
-    expect(click2.mock.calls.length).toBe(0)
-
-    patch(vnode0, vnode1)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(2)
-    expect(click2.mock.calls.length).toBe(1)
-
-    patch(vnode1, vnode2)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(3)
-    expect(click2.mock.calls.length).toBe(1)
-
-    patch(vnode2, vnode3)
-    global.triggerEvent(elm, 'click')
-    expect(click.mock.calls.length).toBe(3)
-    expect(click2.mock.calls.length).toBe(2)
-    expect(click3.mock.calls.length).toBe(1)
-  })
-})
diff --git a/test/unit/modules/vdom/modules/style.spec.ts b/test/unit/modules/vdom/modules/style.spec.ts
deleted file mode 100644
index 19bbb9c8453..00000000000
--- a/test/unit/modules/vdom/modules/style.spec.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { patch } from 'web/runtime/patch'
-import VNode from 'core/vdom/vnode'
-
-describe('vdom style module', () => {
-  it('should create an element with style', () => {
-    const vnode = new VNode('p', { style: { fontSize: '12px' } })
-    const elm = patch(null, vnode)
-    expect(elm.style.fontSize).toBe('12px')
-  })
-
-  it('should create an element with array style', () => {
-    const vnode = new VNode('p', {
-      style: [{ fontSize: '12px' }, { color: 'red' }]
-    })
-    const elm = patch(null, vnode)
-    expect(elm.style.fontSize).toBe('12px')
-    expect(elm.style.color).toBe('red')
-  })
-
-  it('should change elements style', () => {
-    const vnode1 = new VNode('p', { style: { fontSize: '12px' } })
-    const vnode2 = new VNode('p', {
-      style: { fontSize: '10px', display: 'block' }
-    })
-    patch(null, vnode1)
-    const elm = patch(vnode1, vnode2)
-    expect(elm.style.fontSize).toBe('10px')
-    expect(elm.style.display).toBe('block')
-  })
-
-  it('should remove elements attrs', () => {
-    const vnode1 = new VNode('p', { style: { fontSize: '12px' } })
-    const vnode2 = new VNode('p', { style: { display: 'block' } })
-    patch(null, vnode1)
-    const elm = patch(vnode1, vnode2)
-    expect(elm.style.fontSize).toBe('')
-    expect(elm.style.display).toBe('block')
-  })
-
-  it('border related style should update correctly', () => {
-    const vnode1 = new VNode('p', {
-      style: { border: '10px solid red', 'border-bottom': '10px solid blue' }
-    })
-    const vnode2 = new VNode('p', {
-      style: {
-        'border-right': '10px solid red',
-        'border-bottom': '10px solid blue'
-      }
-    })
-    patch(null, vnode1)
-    const elm = patch(vnode1, vnode2)
-    expect(elm.style.borderBottom).toBe('10px solid blue')
-  })
-})
diff --git a/test/unit/modules/vdom/patch/children.spec.ts b/test/unit/modules/vdom/patch/children.spec.ts
deleted file mode 100644
index 2cdc142218e..00000000000
--- a/test/unit/modules/vdom/patch/children.spec.ts
+++ /dev/null
@@ -1,577 +0,0 @@
-import { patch } from 'web/runtime/patch'
-import VNode, { createEmptyVNode } from 'core/vdom/vnode'
-
-function prop(name) {
-  return obj => {
-    return obj[name]
-  }
-}
-
-function map(fn, list) {
-  const ret: any[] = []
-  for (let i = 0; i < list.length; i++) {
-    ret[i] = fn(list[i])
-  }
-  return ret
-}
-
-function spanNum(n) {
-  if (typeof n === 'string') {
-    return new VNode('span', {}, undefined, n)
-  } else {
-    return new VNode('span', { key: n }, undefined, n.toString())
-  }
-}
-
-function shuffle(array) {
-  let currentIndex = array.length
-  let temporaryValue
-  let randomIndex
-
-  // while there remain elements to shuffle...
-  while (currentIndex !== 0) {
-    // pick a remaining element...
-    randomIndex = Math.floor(Math.random() * currentIndex)
-    currentIndex -= 1
-    // and swap it with the current element.
-    temporaryValue = array[currentIndex]
-    array[currentIndex] = array[randomIndex]
-    array[randomIndex] = temporaryValue
-  }
-  return array
-}
-
-const inner = prop('innerHTML')
-const tag = prop('tagName')
-
-describe('vdom patch: children', () => {
-  let vnode0
-  beforeEach(() => {
-    vnode0 = new VNode('p', { attrs: { id: '1' } }, [
-      createTextVNode('hello world')
-    ])
-    patch(null, vnode0)
-  })
-
-  it('should appends elements', () => {
-    const vnode1 = new VNode('p', {}, [1].map(spanNum))
-    const vnode2 = new VNode('p', {}, [1, 2, 3].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(1)
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(3)
-    expect(elm.children[1].innerHTML).toBe('2')
-    expect(elm.children[2].innerHTML).toBe('3')
-  })
-
-  it('should prepends elements', () => {
-    const vnode1 = new VNode('p', {}, [4, 5].map(spanNum))
-    const vnode2 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(2)
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['1', '2', '3', '4', '5'])
-  })
-
-  it('should add elements in the middle', () => {
-    const vnode1 = new VNode('p', {}, [1, 2, 4, 5].map(spanNum))
-    const vnode2 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(4)
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['1', '2', '3', '4', '5'])
-  })
-
-  it('should add elements at begin and end', () => {
-    const vnode1 = new VNode('p', {}, [2, 3, 4].map(spanNum))
-    const vnode2 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(3)
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['1', '2', '3', '4', '5'])
-  })
-
-  it('should add children to parent with no children', () => {
-    const vnode1 = new VNode('p', { key: 'p' })
-    const vnode2 = new VNode('p', { key: 'p' }, [1, 2, 3].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(0)
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['1', '2', '3'])
-  })
-
-  it('should remove all children from parent', () => {
-    const vnode1 = new VNode('p', { key: 'p' }, [1, 2, 3].map(spanNum))
-    const vnode2 = new VNode('p', { key: 'p' })
-    let elm = patch(vnode0, vnode1)
-    expect(map(inner, elm.children)).toEqual(['1', '2', '3'])
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(0)
-  })
-
-  it('should remove elements from the beginning', () => {
-    const vnode1 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))
-    const vnode2 = new VNode('p', {}, [3, 4, 5].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(5)
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['3', '4', '5'])
-  })
-
-  it('should removes elements from end', () => {
-    const vnode1 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))
-    const vnode2 = new VNode('p', {}, [1, 2, 3].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(5)
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(3)
-    expect(map(inner, elm.children)).toEqual(['1', '2', '3'])
-  })
-
-  it('should remove elements from the middle', () => {
-    const vnode1 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))
-    const vnode2 = new VNode('p', {}, [1, 2, 4, 5].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(5)
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(4)
-    expect(map(inner, elm.children)).toEqual(['1', '2', '4', '5'])
-  })
-
-  it('should moves element forward', () => {
-    const vnode1 = new VNode('p', {}, [1, 2, 3, 4].map(spanNum))
-    const vnode2 = new VNode('p', {}, [2, 3, 1, 4].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(4)
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(4)
-    expect(map(inner, elm.children)).toEqual(['2', '3', '1', '4'])
-  })
-
-  it('should move elements to end', () => {
-    const vnode1 = new VNode('p', {}, [1, 2, 3].map(spanNum))
-    const vnode2 = new VNode('p', {}, [2, 3, 1].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(3)
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(3)
-    expect(map(inner, elm.children)).toEqual(['2', '3', '1'])
-  })
-
-  it('should move element backwards', () => {
-    const vnode1 = new VNode('p', {}, [1, 2, 3, 4].map(spanNum))
-    const vnode2 = new VNode('p', {}, [1, 4, 2, 3].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(4)
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(4)
-    expect(map(inner, elm.children)).toEqual(['1', '4', '2', '3'])
-  })
-
-  it('should swap first and last', () => {
-    const vnode1 = new VNode('p', {}, [1, 2, 3, 4].map(spanNum))
-    const vnode2 = new VNode('p', {}, [4, 2, 3, 1].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(4)
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(4)
-    expect(map(inner, elm.children)).toEqual(['4', '2', '3', '1'])
-  })
-
-  it('should move to left and replace', () => {
-    const vnode1 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))
-    const vnode2 = new VNode('p', {}, [4, 1, 2, 3, 6].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(5)
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(5)
-    expect(map(inner, elm.children)).toEqual(['4', '1', '2', '3', '6'])
-  })
-
-  it('should move to left and leaves hold', () => {
-    const vnode1 = new VNode('p', {}, [1, 4, 5].map(spanNum))
-    const vnode2 = new VNode('p', {}, [4, 6].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(3)
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['4', '6'])
-  })
-
-  it('should handle moved and set to undefined element ending at the end', () => {
-    const vnode1 = new VNode('p', {}, [2, 4, 5].map(spanNum))
-    const vnode2 = new VNode('p', {}, [4, 5, 3].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(3)
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(3)
-    expect(map(inner, elm.children)).toEqual(['4', '5', '3'])
-  })
-
-  it('should move a key in non-keyed nodes with a size up', () => {
-    const vnode1 = new VNode('p', {}, [1, 'a', 'b', 'c'].map(spanNum))
-    const vnode2 = new VNode('p', {}, ['d', 'a', 'b', 'c', 1, 'e'].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(4)
-    expect(elm.textContent, '1abc')
-    elm = patch(vnode1, vnode2)
-    expect(elm.children.length).toBe(6)
-    expect(elm.textContent, 'dabc1e')
-  })
-
-  it('should reverse element', () => {
-    const vnode1 = new VNode('p', {}, [1, 2, 3, 4, 5, 6, 7, 8].map(spanNum))
-    const vnode2 = new VNode('p', {}, [8, 7, 6, 5, 4, 3, 2, 1].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(8)
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual([
-      '8',
-      '7',
-      '6',
-      '5',
-      '4',
-      '3',
-      '2',
-      '1'
-    ])
-  })
-
-  it('something', () => {
-    const vnode1 = new VNode('p', {}, [0, 1, 2, 3, 4, 5].map(spanNum))
-    const vnode2 = new VNode('p', {}, [4, 3, 2, 1, 5, 0].map(spanNum))
-    let elm = patch(vnode0, vnode1)
-    expect(elm.children.length).toBe(6)
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['4', '3', '2', '1', '5', '0'])
-  })
-
-  it('should handle random shuffle', () => {
-    let n
-    let i
-    const arr: any[] = []
-    const opacities: any[] = []
-    const elms = 14
-    const samples = 5
-    function spanNumWithOpacity(n, o) {
-      return new VNode(
-        'span',
-        { key: n, style: { opacity: o } },
-        undefined,
-        n.toString()
-      )
-    }
-
-    for (n = 0; n < elms; ++n) {
-      arr[n] = n
-    }
-    for (n = 0; n < samples; ++n) {
-      const vnode1 = new VNode(
-        'span',
-        {},
-        arr.map(n => {
-          return spanNumWithOpacity(n, '1')
-        })
-      )
-      const shufArr = shuffle(arr.slice(0))
-      let elm = patch(vnode0, vnode1)
-      for (i = 0; i < elms; ++i) {
-        expect(elm.children[i].innerHTML).toBe(i.toString())
-        opacities[i] = Math.random().toFixed(5).toString()
-      }
-      const vnode2 = new VNode(
-        'span',
-        {},
-        arr.map(n => {
-          return spanNumWithOpacity(shufArr[n], opacities[n])
-        })
-      )
-      elm = patch(vnode1, vnode2)
-      for (i = 0; i < elms; ++i) {
-        expect(elm.children[i].innerHTML).toBe(shufArr[i].toString())
-        expect(opacities[i].indexOf(elm.children[i].style.opacity)).toBe(0)
-      }
-    }
-  })
-
-  it('should append elements with updating children without keys', () => {
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'hello')
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'hello'),
-      new VNode('span', {}, undefined, 'world')
-    ])
-    let elm = patch(vnode0, vnode1)
-    expect(map(inner, elm.children)).toEqual(['hello'])
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['hello', 'world'])
-  })
-
-  it('should handle unmoved text nodes with updating children without keys', () => {
-    const vnode1 = new VNode('div', {}, [
-      createTextVNode('text'),
-      new VNode('span', {}, undefined, 'hello')
-    ])
-    const vnode2 = new VNode('div', {}, [
-      createTextVNode('text'),
-      new VNode('span', {}, undefined, 'hello')
-    ])
-    let elm = patch(vnode0, vnode1)
-    expect(elm.childNodes[0].textContent).toBe('text')
-    elm = patch(vnode1, vnode2)
-    expect(elm.childNodes[0].textContent).toBe('text')
-  })
-
-  it('should handle changing text children with updating children without keys', () => {
-    const vnode1 = new VNode('div', {}, [
-      createTextVNode('text'),
-      new VNode('span', {}, undefined, 'hello')
-    ])
-    const vnode2 = new VNode('div', {}, [
-      createTextVNode('text2'),
-      new VNode('span', {}, undefined, 'hello')
-    ])
-    let elm = patch(vnode0, vnode1)
-    expect(elm.childNodes[0].textContent).toBe('text')
-    elm = patch(vnode1, vnode2)
-    expect(elm.childNodes[0].textContent).toBe('text2')
-  })
-
-  it('should prepend element with updating children without keys', () => {
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'world')
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'hello'),
-      new VNode('span', {}, undefined, 'world')
-    ])
-    let elm = patch(vnode0, vnode1)
-    expect(map(inner, elm.children)).toEqual(['world'])
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['hello', 'world'])
-  })
-
-  it('should prepend element of different tag type with updating children without keys', () => {
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'world')
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('div', {}, undefined, 'hello'),
-      new VNode('span', {}, undefined, 'world')
-    ])
-    let elm = patch(vnode0, vnode1)
-    expect(map(inner, elm.children)).toEqual(['world'])
-    elm = patch(vnode1, vnode2)
-    expect(map(prop('tagName'), elm.children)).toEqual(['DIV', 'SPAN'])
-    expect(map(inner, elm.children)).toEqual(['hello', 'world'])
-  })
-
-  it('should remove elements with updating children without keys', () => {
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'one'),
-      new VNode('span', {}, undefined, 'two'),
-      new VNode('span', {}, undefined, 'three')
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'one'),
-      new VNode('span', {}, undefined, 'three')
-    ])
-    let elm = patch(vnode0, vnode1)
-    expect(map(inner, elm.children)).toEqual(['one', 'two', 'three'])
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['one', 'three'])
-  })
-
-  it('should remove a single text node with updating children without keys', () => {
-    const vnode1 = new VNode('div', {}, undefined, 'one')
-    const vnode2 = new VNode('div', {})
-    let elm = patch(vnode0, vnode1)
-    expect(elm.textContent).toBe('one')
-    elm = patch(vnode1, vnode2)
-    expect(elm.textContent).toBe('')
-  })
-
-  it('should remove a single text node when children are updated', () => {
-    const vnode1 = new VNode('div', {}, undefined, 'one')
-    const vnode2 = new VNode('div', {}, [
-      new VNode('div', {}, undefined, 'two'),
-      new VNode('span', {}, undefined, 'three')
-    ])
-    let elm = patch(vnode0, vnode1)
-    expect(elm.textContent).toBe('one')
-    elm = patch(vnode1, vnode2)
-    expect(map(prop('textContent'), elm.childNodes)).toEqual(['two', 'three'])
-  })
-
-  it('should remove a text node among other elements', () => {
-    const vnode1 = new VNode('div', {}, [
-      createTextVNode('one'),
-      new VNode('span', {}, undefined, 'two')
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('div', {}, undefined, 'three')
-    ])
-    let elm = patch(vnode0, vnode1)
-    expect(map(prop('textContent'), elm.childNodes)).toEqual(['one', 'two'])
-    elm = patch(vnode1, vnode2)
-    expect(elm.childNodes.length).toBe(1)
-    expect(elm.childNodes[0].tagName).toBe('DIV')
-    expect(elm.childNodes[0].textContent).toBe('three')
-  })
-
-  it('should reorder elements', () => {
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'one'),
-      new VNode('div', {}, undefined, 'two'),
-      new VNode('b', {}, undefined, 'three')
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('b', {}, undefined, 'three'),
-      new VNode('span', {}, undefined, 'two'),
-      new VNode('div', {}, undefined, 'one')
-    ])
-    let elm = patch(vnode0, vnode1)
-    expect(map(inner, elm.children)).toEqual(['one', 'two', 'three'])
-    elm = patch(vnode1, vnode2)
-    expect(map(inner, elm.children)).toEqual(['three', 'two', 'one'])
-  })
-
-  it('should handle children with the same key but with different tag', () => {
-    const vnode1 = new VNode('div', {}, [
-      new VNode('div', { key: 1 }, undefined, 'one'),
-      new VNode('div', { key: 2 }, undefined, 'two'),
-      new VNode('div', { key: 3 }, undefined, 'three'),
-      new VNode('div', { key: 4 }, undefined, 'four')
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('div', { key: 4 }, undefined, 'four'),
-      new VNode('span', { key: 3 }, undefined, 'three'),
-      new VNode('span', { key: 2 }, undefined, 'two'),
-      new VNode('div', { key: 1 }, undefined, 'one')
-    ])
-    let elm = patch(vnode0, vnode1)
-    expect(map(tag, elm.children)).toEqual(['DIV', 'DIV', 'DIV', 'DIV'])
-    expect(map(inner, elm.children)).toEqual(['one', 'two', 'three', 'four'])
-    elm = patch(vnode1, vnode2)
-    expect(map(tag, elm.children)).toEqual(['DIV', 'SPAN', 'SPAN', 'DIV'])
-    expect(map(inner, elm.children)).toEqual(['four', 'three', 'two', 'one'])
-  })
-
-  it('should handle children with the same tag, same key, but one with data and one without data', () => {
-    const vnode1 = new VNode('div', {}, [
-      new VNode('div', { class: 'hi' }, undefined, 'one')
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('div', undefined, undefined, 'four')
-    ])
-    let elm = patch(vnode0, vnode1)
-    const child1 = elm.children[0]
-    expect(child1.className).toBe('hi')
-    elm = patch(vnode1, vnode2)
-    const child2 = elm.children[0]
-    expect(child1).not.toBe(child2)
-    expect(child2.className).toBe('')
-  })
-
-  it('should handle static vnodes properly', () => {
-    function makeNode(text) {
-      return new VNode('div', undefined, [
-        new VNode(undefined, undefined, undefined, text)
-      ])
-    }
-    const b = makeNode('B')
-    b.isStatic = true
-    b.key = `__static__1`
-    const vnode1 = new VNode('div', {}, [makeNode('A'), b, makeNode('C')])
-    const vnode2 = new VNode('div', {}, [b])
-    const vnode3 = new VNode('div', {}, [makeNode('A'), b, makeNode('C')])
-
-    let elm = patch(vnode0, vnode1)
-    expect(elm.textContent).toBe('ABC')
-    elm = patch(vnode1, vnode2)
-    expect(elm.textContent).toBe('B')
-    elm = patch(vnode2, vnode3)
-    expect(elm.textContent).toBe('ABC')
-  })
-
-  it('should handle static vnodes inside ', () => {
-    function makeNode(text) {
-      return new VNode('div', undefined, [
-        new VNode(undefined, undefined, undefined, text)
-      ])
-    }
-    const b = makeNode('B')
-    b.isStatic = true
-    b.key = `__static__1`
-    const vnode1 = new VNode('div', {}, [makeNode('A'), b, makeNode('C')])
-    const vnode2 = new VNode('div', {}, [b])
-    const vnode3 = new VNode('div', {}, [makeNode('A'), b, makeNode('C')])
-
-    let elm = patch(vnode0, vnode1)
-    expect(elm.textContent).toBe('ABC')
-    elm = patch(vnode1, vnode2)
-    expect(elm.textContent).toBe('B')
-    elm = patch(vnode2, vnode3)
-    expect(elm.textContent).toBe('ABC')
-  })
-
-  // #6502
-  it('should not de-opt when both head and tail are changed', () => {
-    const vnode1 = new VNode('div', {}, [
-      createEmptyVNode(),
-      new VNode('div'),
-      createEmptyVNode()
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('p'),
-      new VNode('div'),
-      new VNode('p')
-    ])
-    let root = patch(null, vnode1)
-    const original = root.childNodes[1]
-
-    root = patch(vnode1, vnode2)
-    const postPatch = root.childNodes[1]
-
-    expect(postPatch).toBe(original)
-  })
-
-  it('should warn with duplicate keys: createChildren', () => {
-    function makeNode(key) {
-      return new VNode('div', { key: key })
-    }
-
-    const vnode = new VNode('p', {}, ['b', 'a', 'c', 'b'].map(makeNode))
-    patch(null, vnode)
-    expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned()
-  })
-
-  it('should warn with duplicate keys: updateChildren', () => {
-    function makeNode(key) {
-      return new VNode('div', { key: key })
-    }
-
-    const vnode2 = new VNode('p', {}, ['b', 'a', 'c', 'b'].map(makeNode))
-    const vnode3 = new VNode('p', {}, ['b', 'x', 'd', 'b'].map(makeNode))
-    patch(vnode0, vnode2)
-    expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned()
-    patch(vnode2, vnode3)
-    expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned()
-  })
-
-  it('should warn with duplicate keys: patchVnode with empty oldVnode', () => {
-    function makeNode(key) {
-      return new VNode('li', { key: key })
-    }
-
-    const vnode1 = new VNode('div')
-    const vnode2 = new VNode(
-      'div',
-      undefined,
-      ['1', '2', '3', '4', '4'].map(makeNode)
-    )
-
-    patch(vnode1, vnode2)
-    expect(`Duplicate keys detected: '4'`).toHaveBeenWarned()
-  })
-})
diff --git a/test/unit/modules/vdom/patch/edge-cases.spec.ts b/test/unit/modules/vdom/patch/edge-cases.spec.ts
deleted file mode 100644
index 1b27f2b630c..00000000000
--- a/test/unit/modules/vdom/patch/edge-cases.spec.ts
+++ /dev/null
@@ -1,508 +0,0 @@
-import Vue from 'vue'
-import { SSR_ATTR } from 'shared/constants'
-
-describe('vdom patch: edge cases', () => {
-  // exposed by #3406
-  // When a static vnode is inside v-for, it's possible for the same vnode
-  // to be used in multiple places, and its element will be replaced. This
-  // causes patch errors when node ops depend on the vnode's element position.
-  it('should handle static vnodes by key', done => {
-    const vm = new Vue({
-      data: {
-        ok: true
-      },
-      template: `
-        <div>
-          <div v-for="i in 2">
-            <div v-if="ok">a</div><div>b</div><div v-if="!ok">c</div><div>d</div>
-          </div>
-        </div>
-      `
-    }).$mount()
-    expect(vm.$el.textContent).toBe('abdabd')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toBe('bcdbcd')
-    }).then(done)
-  })
-
-  // exposed by #7705
-  // methods and function expressions with modifiers should return result instead of undefined
-  // skipped odd children[1,3, ...] because they are rendered as text nodes with undefined value
-  it("should return listener's result for method name and function expression with and w/o modifiers", done => {
-    const dummyEvt = { preventDefault: () => {} }
-    new Vue({
-      template: `
-        <div v-test>
-          <div @click="addFive"></div>
-          <div @click.prevent="addFive"></div>
-          <div @click="addFive($event, 5)"></div>
-          <div @click.prevent="addFive($event, 5)"></div>
-        </div>
-      `,
-      methods: {
-        addFive($event, toAdd = 0) {
-          return toAdd + 5
-        }
-      },
-      directives: {
-        test: {
-          bind(el, binding, vnode) {
-            waitForUpdate(() => {
-              expect(vnode.children[0].data.on.click()).toBe(5)
-              expect(vnode.children[2].data.on.click(dummyEvt)).toBe(5)
-              expect(vnode.children[4].data.on.click()).toBe(10)
-              expect(vnode.children[6].data.on.click(dummyEvt)).toBe(10)
-            }).then(done)
-          }
-        }
-      }
-    }).$mount()
-  })
-
-  // #3533
-  // a static node is reused in createElm, which changes its elm reference
-  // and is inserted into a different parent.
-  // later when patching the next element a DOM insertion uses it as the
-  // reference node, causing a parent mismatch.
-  it("should handle static node edge case when it's reused AND used as a reference node for insertion", done => {
-    const vm = new Vue({
-      data: {
-        ok: true
-      },
-      template: `
-        <div>
-          <button @click="ok = !ok">toggle</button>
-          <div class="b" v-if="ok">123</div>
-          <div class="c">
-            <div><span/></div><p>{{ 1 }}</p>
-          </div>
-          <div class="d">
-            <label>{{ 2 }}</label>
-          </div>
-          <div class="b" v-if="ok">123</div>
-        </div>
-      `
-    }).$mount()
-
-    expect(vm.$el.querySelector('.c').textContent).toBe('1')
-    expect(vm.$el.querySelector('.d').textContent).toBe('2')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.querySelector('.c').textContent).toBe('1')
-      expect(vm.$el.querySelector('.d').textContent).toBe('2')
-    }).then(done)
-  })
-
-  it('should handle slot nodes being reused across render', done => {
-    const vm = new Vue({
-      template: `
-        <foo ref="foo">
-          <div>slot</div>
-        </foo>
-      `,
-      components: {
-        foo: {
-          data() {
-            return { ok: true }
-          },
-          render(h) {
-            const children = [
-              this.ok ? h('div', 'toggler ') : null,
-              h('div', [this.$slots.default, h('span', ' 1')]),
-              h('div', [h('label', ' 2')])
-            ]
-            return h('div', children)
-          }
-        }
-      }
-    }).$mount()
-    expect(vm.$el.textContent).toContain('toggler slot 1 2')
-    vm.$refs.foo.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toContain('slot 1 2')
-      vm.$refs.foo.ok = true
-    })
-      .then(() => {
-        expect(vm.$el.textContent).toContain('toggler slot 1 2')
-        vm.$refs.foo.ok = false
-      })
-      .then(() => {
-        expect(vm.$el.textContent).toContain('slot 1 2')
-        vm.$refs.foo.ok = true
-      })
-      .then(done)
-  })
-
-  it("should synchronize vm' vnode", done => {
-    const comp = {
-      data: () => ({ swap: true }),
-      render(h) {
-        return this.swap ? h('a', 'atag') : h('span', 'span')
-      }
-    }
-
-    const wrapper = {
-      render: h => h('comp'),
-      components: { comp }
-    }
-
-    const vm = new Vue({
-      render(h) {
-        const children = [h('wrapper'), h('div', 'row')]
-        if (this.swap) {
-          children.reverse()
-        }
-        return h('div', children)
-      },
-      data: () => ({ swap: false }),
-      components: { wrapper }
-    }).$mount()
-
-    expect(vm.$el.innerHTML).toBe('<a>atag</a><div>row</div>')
-    const wrapperVm = vm.$children[0]
-    const compVm = wrapperVm.$children[0]
-    vm.swap = true
-    waitForUpdate(() => {
-      expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
-      expect(vm.$el.innerHTML).toBe('<div>row</div><a>atag</a>')
-      vm.swap = false
-    })
-      .then(() => {
-        expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
-        expect(vm.$el.innerHTML).toBe('<a>atag</a><div>row</div>')
-        compVm.swap = false
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<span>span</span><div>row</div>')
-        expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
-        vm.swap = true
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe('<div>row</div><span>span</span>')
-        expect(compVm.$vnode.parent).toBe(wrapperVm.$vnode)
-        vm.swap = true
-      })
-      .then(done)
-  })
-
-  // #4530
-  it('should not reset value when patching between dynamic/static bindings', done => {
-    const vm = new Vue({
-      data: { ok: true },
-      template: `
-        <div>
-          <input type="button" v-if="ok" value="a">
-          <input type="button" :value="'b'">
-        </div>
-      `
-    }).$mount()
-    expect(vm.$el.children[0].value).toBe('a')
-    vm.ok = false
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].value).toBe('b')
-      vm.ok = true
-    })
-      .then(() => {
-        expect(vm.$el.children[0].value).toBe('a')
-      })
-      .then(done)
-  })
-
-  // #6313
-  it('should not replace node when switching between text-like inputs', done => {
-    const vm = new Vue({
-      data: { show: false },
-      template: `
-        <div>
-          <input :type="show ? 'text' : 'password'">
-        </div>
-      `
-    }).$mount()
-    const node = vm.$el.children[0]
-    expect(vm.$el.children[0].type).toBe('password')
-    vm.$el.children[0].value = 'test'
-    vm.show = true
-    waitForUpdate(() => {
-      expect(vm.$el.children[0]).toBe(node)
-      expect(vm.$el.children[0].value).toBe('test')
-      expect(vm.$el.children[0].type).toBe('text')
-      vm.show = false
-    })
-      .then(() => {
-        expect(vm.$el.children[0]).toBe(node)
-        expect(vm.$el.children[0].value).toBe('test')
-        expect(vm.$el.children[0].type).toBe('password')
-      })
-      .then(done)
-  })
-
-  it('should properly patch nested HOC when root element is replaced', done => {
-    const vm = new Vue({
-      template: `<foo class="hello" ref="foo" />`,
-      components: {
-        foo: {
-          template: `<bar ref="bar" />`,
-          components: {
-            bar: {
-              template: `<div v-if="ok"></div><span v-else></span>`,
-              data() {
-                return { ok: true }
-              }
-            }
-          }
-        }
-      }
-    }).$mount()
-
-    expect(vm.$refs.foo.$refs.bar.$el.tagName).toBe('DIV')
-    expect(vm.$refs.foo.$refs.bar.$el.className).toBe(`hello`)
-    expect(vm.$el.tagName).toBe('DIV')
-    expect(vm.$el.className).toBe(`hello`)
-
-    vm.$refs.foo.$refs.bar.ok = false
-    waitForUpdate(() => {
-      expect(vm.$refs.foo.$refs.bar.$el.tagName).toBe('SPAN')
-      expect(vm.$refs.foo.$refs.bar.$el.className).toBe(`hello`)
-      expect(vm.$el.tagName).toBe('SPAN')
-      expect(vm.$el.className).toBe(`hello`)
-    }).then(done)
-  })
-
-  // #6790
-  it('should not render undefined for empty nested arrays', () => {
-    const vm = new Vue({
-      template: `<div><template v-for="i in emptyArr"></template></div>`,
-      data: { emptyArr: [] }
-    }).$mount()
-    expect(vm.$el.textContent).toBe('')
-  })
-
-  // #6803
-  it('backwards compat with checkbox code generated before 2.4', () => {
-    const spy = vi.fn()
-    const vm = new Vue({
-      data: {
-        label: 'foobar',
-        name: 'foobar'
-      },
-      computed: {
-        value: {
-          get() {
-            return 1
-          },
-          set: spy
-        }
-      },
-      render(h) {
-        const _vm = this
-        return h('div', {}, [
-          h('input', {
-            directives: [
-              {
-                name: 'model',
-                rawName: 'v-model',
-                value: _vm.value,
-                expression: 'value'
-              }
-            ],
-            attrs: {
-              type: 'radio',
-              name: _vm.name
-            },
-            domProps: {
-              value: _vm.label,
-              checked: _vm._q(_vm.value, _vm.label)
-            },
-            on: {
-              __c: function ($event) {
-                _vm.value = _vm.label
-              }
-            }
-          })
-        ])
-      }
-    }).$mount()
-
-    document.body.appendChild(vm.$el)
-    vm.$el.children[0].click()
-    expect(spy).toHaveBeenCalled()
-  })
-
-  // #7041
-  it('transition children with only deep bindings should be patched on update', done => {
-    const vm = new Vue({
-      template: `
-      <div>
-        <transition>
-          <div :style="style"></div>
-        </transition>
-      </div>
-      `,
-      data: () => ({
-        style: { color: 'red' }
-      })
-    }).$mount()
-    expect(vm.$el.children[0].style.color).toBe('red')
-    vm.style.color = 'green'
-    waitForUpdate(() => {
-      expect(vm.$el.children[0].style.color).toBe('green')
-    }).then(done)
-  })
-
-  // #7294
-  it('should cleanup component inline events on patch when no events are present', done => {
-    const log = vi.fn()
-    const vm = new Vue({
-      data: { ok: true },
-      template: `
-        <div>
-          <foo v-if="ok" @custom="log"/>
-          <foo v-else/>
-        </div>
-      `,
-      components: {
-        foo: {
-          render() {}
-        }
-      },
-      methods: { log }
-    }).$mount()
-
-    vm.ok = false
-    waitForUpdate(() => {
-      vm.$children[0].$emit('custom')
-      expect(log).not.toHaveBeenCalled()
-    }).then(done)
-  })
-
-  // #6864
-  it('should not special-case boolean attributes for custom elements', () => {
-    Vue.config.ignoredElements = [/^custom-/]
-    const vm = new Vue({
-      template: `<div><custom-foo selected="1"/></div>`
-    }).$mount()
-    expect(vm.$el.querySelector('custom-foo').getAttribute('selected')).toBe(
-      '1'
-    )
-    Vue.config.ignoredElements = []
-  })
-
-  // #7805
-  it('should not cause duplicate init when components share data object', () => {
-    const Base = {
-      render(h) {
-        return h('div', this.$options.name)
-      }
-    }
-
-    const Foo = {
-      name: 'Foo',
-      extends: Base
-    }
-
-    const Bar = {
-      name: 'Bar',
-      extends: Base
-    }
-
-    // sometimes we do need to tap into these internal hooks (e.g. in vue-router)
-    // so make sure it does work
-    const inlineHookSpy = vi.fn()
-
-    const vm = new Vue({
-      render(h) {
-        const data = {
-          staticClass: 'text-red',
-          hook: {
-            init: inlineHookSpy
-          }
-        }
-
-        return h('div', [h(Foo, data), h(Bar, data)])
-      }
-    }).$mount()
-
-    expect(vm.$el.textContent).toBe('FooBar')
-    expect(inlineHookSpy.mock.calls.length).toBe(2)
-  })
-
-  // #9549
-  it('DOM props set throwing should not break app', done => {
-    const vm = new Vue({
-      data: {
-        n: Infinity
-      },
-      template: `
-        <div>
-          <progress :value="n"/>
-          {{ n }}
-        </div>
-      `
-    }).$mount()
-
-    expect(vm.$el.textContent).toMatch('Infinity')
-    vm.n = 1
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toMatch('1')
-      expect(vm.$el.textContent).not.toMatch('Infinity')
-    }).then(done)
-  })
-
-  it('should not throw when hydrated pending async component is patched by v-if="false"', done => {
-    const PendingAsyncComponent = () => new Promise(() => {})
-    const ssrAsyncComponent = document.createElement('div')
-    ssrAsyncComponent.setAttribute(SSR_ATTR, 'true')
-    const vm = new Vue({
-      data: {
-        visible: true
-      },
-      components: {
-        PendingAsyncComponent
-      },
-      template:
-        '<pending-async-component v-if="visible"></pending-async-component>'
-    }).$mount(ssrAsyncComponent)
-
-    vm.visible = false
-    vm.$nextTick(done)
-  })
-
-  it('should call directive\'s inserted hook correctly when template root is changed', done => {
-    let insertCalled = false
-    const vm = new Vue({
-      data: {
-        isOn: false
-      },
-      components: {
-        'v-switch': {
-          props: {
-            on: Boolean
-          },
-          template: `
-            <div v-if="on" key="on">On</div>
-            <div v-else key="off">Off</div>`
-        }
-      },
-      directives: {
-        foo: {
-          inserted() {
-            insertCalled = true
-          }
-        }
-      },
-      template: `
-        <transition-group>
-          <v-switch key="swicth" v-foo :on="isOn"/>
-        </transition-group>
-      `
-    }).$mount()
-
-    vm.isOn = true
-    insertCalled = false
-    waitForUpdate(() => {
-      expect(vm.$el.textContent).toMatch('On')
-      expect(insertCalled).toBe(true)
-    }).then(done)
-  })
-})
diff --git a/test/unit/modules/vdom/patch/element.spec.ts b/test/unit/modules/vdom/patch/element.spec.ts
deleted file mode 100644
index 2838b87da94..00000000000
--- a/test/unit/modules/vdom/patch/element.spec.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import Vue from 'vue'
-import { patch } from 'web/runtime/patch'
-import VNode from 'core/vdom/vnode'
-
-describe('vdom patch: element', () => {
-  it('should create an element', () => {
-    const vnode = new VNode('p', { attrs: { id: '1' } }, [
-      createTextVNode('hello world')
-    ])
-    const elm = patch(null, vnode)
-    expect(elm.tagName).toBe('P')
-    expect(elm.outerHTML).toBe('<p id="1">hello world</p>')
-  })
-
-  it('should create an element which having the namespace', () => {
-    const vnode = new VNode('svg', {})
-    vnode.ns = 'svg'
-    const elm = patch(null, vnode)
-    expect(elm.namespaceURI).toBe('http://www.w3.org/2000/svg')
-  })
-
-  const el = document.createElement('unknown')
-  // Android Browser <= 4.2 doesn't use correct class name,
-  // but it doesn't matter because no one's gonna use it as their primary
-  // development browser.
-  if (/HTMLUnknownElement/.test(el.toString())) {
-    it('should warn unknown element', () => {
-      const vnode = new VNode('unknown')
-      patch(null, vnode)
-      expect(`Unknown custom element: <unknown>`).toHaveBeenWarned()
-    })
-  }
-
-  it('should warn unknown element with hyphen', () => {
-    const vnode = new VNode('unknown-foo')
-    patch(null, vnode)
-    expect(`Unknown custom element: <unknown-foo>`).toHaveBeenWarned()
-  })
-
-  it('should create an elements which having text content', () => {
-    const vnode = new VNode('div', {}, [createTextVNode('hello world')])
-    const elm = patch(null, vnode)
-    expect(elm.innerHTML).toBe('hello world')
-  })
-
-  it('should create an elements which having span and text content', () => {
-    const vnode = new VNode('div', {}, [
-      new VNode('span'),
-      createTextVNode('hello world')
-    ])
-    const elm = patch(null, vnode)
-    expect(elm.childNodes[0].tagName).toBe('SPAN')
-    expect(elm.childNodes[1].textContent).toBe('hello world')
-  })
-
-  it('should create element with scope attribute', () => {
-    const vnode = new VNode('div')
-    vnode.context = new Vue({ _scopeId: 'foo' })
-    const elm = patch(null, vnode)
-    expect(elm.hasAttribute('foo')).toBe(true)
-  })
-})
diff --git a/test/unit/modules/vdom/patch/hooks.spec.ts b/test/unit/modules/vdom/patch/hooks.spec.ts
deleted file mode 100644
index ba80c700739..00000000000
--- a/test/unit/modules/vdom/patch/hooks.spec.ts
+++ /dev/null
@@ -1,378 +0,0 @@
-import { patch } from 'web/runtime/patch'
-import { createPatchFunction } from 'core/vdom/patch'
-import baseModules from 'core/vdom/modules/index'
-import * as nodeOps from 'web/runtime/node-ops'
-import platformModules from 'web/runtime/modules/index'
-import VNode from 'core/vdom/vnode'
-
-const modules = baseModules.concat(platformModules) as any[]
-
-describe('vdom patch: hooks', () => {
-  let vnode0
-  beforeEach(() => {
-    vnode0 = new VNode('p', { attrs: { id: '1' } }, [
-      createTextVNode('hello world')
-    ])
-    patch(null, vnode0)
-  })
-
-  it('should call `insert` listener after both parents, siblings and children have been inserted', () => {
-    const result: any[] = []
-    function insert(vnode) {
-      expect(vnode.elm.children.length).toBe(2)
-      expect(vnode.elm.parentNode.children.length).toBe(3)
-      result.push(vnode)
-    }
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', { hook: { insert } }, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode('span', {}, undefined, 'child 2')
-      ]),
-      new VNode('span', {}, undefined, 'can touch me')
-    ])
-    patch(vnode0, vnode1)
-    expect(result.length).toBe(1)
-  })
-
-  it('should call `prepatch` listener', () => {
-    const result: any[] = []
-    function prepatch(oldVnode, newVnode) {
-      expect(oldVnode).toEqual(vnode1.children[1])
-      expect(newVnode).toEqual(vnode2.children[1])
-      result.push(newVnode)
-    }
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', { hook: { prepatch } }, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode('span', {}, undefined, 'child 2')
-      ])
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', { hook: { prepatch } }, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode('span', {}, undefined, 'child 2')
-      ])
-    ])
-    patch(vnode0, vnode1)
-    patch(vnode1, vnode2)
-    expect(result.length).toBe(1)
-  })
-
-  it('should call `postpatch` after `prepatch` listener', () => {
-    const pre: any[] = []
-    const post: any[] = []
-    function prepatch(oldVnode, newVnode) {
-      pre.push(pre)
-    }
-    function postpatch(oldVnode, newVnode) {
-      expect(pre.length).toBe(post.length + 1)
-      post.push(post)
-    }
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', { hook: { prepatch, postpatch } }, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode('span', {}, undefined, 'child 2')
-      ])
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', { hook: { prepatch, postpatch } }, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode('span', {}, undefined, 'child 2')
-      ])
-    ])
-    patch(vnode0, vnode1)
-    patch(vnode1, vnode2)
-    expect(pre.length).toBe(1)
-    expect(post.length).toBe(1)
-  })
-
-  it('should call `update` listener', () => {
-    const result1: any[] = []
-    const result2: any[] = []
-    function cb(result, oldVnode, newVnode) {
-      if (result.length > 1) {
-        expect(result[result.length - 1]).toEqual(oldVnode)
-      }
-      result.push(newVnode)
-    }
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', { hook: { update: cb.bind(null, result1) } }, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode(
-          'span',
-          { hook: { update: cb.bind(null, result2) } },
-          undefined,
-          'child 2'
-        )
-      ])
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', { hook: { update: cb.bind(null, result1) } }, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode(
-          'span',
-          { hook: { update: cb.bind(null, result2) } },
-          undefined,
-          'child 2'
-        )
-      ])
-    ])
-    patch(vnode0, vnode1)
-    patch(vnode1, vnode2)
-    expect(result1.length).toBe(1)
-    expect(result2.length).toBe(1)
-  })
-
-  it('should call `remove` listener', () => {
-    const result: any[] = []
-    function remove(vnode, rm) {
-      const parent = vnode.elm.parentNode
-      expect(vnode.elm.children.length).toBe(2)
-      expect(vnode.elm.children.length).toBe(2)
-      result.push(vnode)
-      rm()
-      expect(parent.children.length).toBe(1)
-    }
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', { hook: { remove } }, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode('span', {}, undefined, 'child 2')
-      ])
-    ])
-    const vnode2 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling')
-    ])
-    patch(vnode0, vnode1)
-    patch(vnode1, vnode2)
-    expect(result.length).toBe(1)
-  })
-
-  it('should call `init` and `prepatch` listeners on root', () => {
-    let count = 0
-    function init(vnode) {
-      count++
-    }
-    function prepatch(oldVnode, newVnode) {
-      count++
-    }
-    const vnode1 = new VNode('div', { hook: { init, prepatch } })
-    patch(vnode0, vnode1)
-    expect(count).toBe(1)
-    const vnode2 = new VNode('span', { hook: { init, prepatch } })
-    patch(vnode1, vnode2)
-    expect(count).toBe(2)
-  })
-
-  it('should remove element when all remove listeners are done', () => {
-    let rm1, rm2, rm3
-    const patch1 = createPatchFunction({
-      nodeOps,
-      // @ts-ignore - TODO dtw
-      modules: modules.concat([
-        {
-          remove(_, rm) {
-            rm1 = rm
-          }
-        },
-        {
-          remove(_, rm) {
-            rm2 = rm
-          }
-        }
-      ])
-    })
-    const vnode1 = new VNode('div', {}, [
-      new VNode('a', {
-        hook: {
-          remove(_, rm) {
-            rm3 = rm
-          }
-        }
-      })
-    ])
-    const vnode2 = new VNode('div', {}, [])
-    let elm = patch1(vnode0, vnode1)
-    expect(elm.children.length).toBe(1)
-    elm = patch1(vnode1, vnode2)
-    expect(elm.children.length).toBe(1)
-    rm1()
-    expect(elm.children.length).toBe(1)
-    rm3()
-    expect(elm.children.length).toBe(1)
-    rm2()
-    expect(elm.children.length).toBe(0)
-  })
-
-  it('should invoke the remove hook on replaced root', () => {
-    const result: any[] = []
-    const parent = nodeOps.createElement('div')
-    vnode0 = nodeOps.createElement('div')
-    parent.appendChild(vnode0)
-    function remove(vnode, rm) {
-      result.push(vnode)
-      rm()
-    }
-    const vnode1 = new VNode('div', { hook: { remove } }, [
-      new VNode('b', {}, undefined, 'child 1'),
-      new VNode('i', {}, undefined, 'child 2')
-    ])
-    const vnode2 = new VNode('span', {}, [
-      new VNode('b', {}, undefined, 'child 1'),
-      new VNode('i', {}, undefined, 'child 2')
-    ])
-    patch(vnode0, vnode1)
-    patch(vnode1, vnode2)
-    expect(result.length).toBe(1)
-  })
-
-  it('should invoke global `destroy` hook for all removed children', () => {
-    const result: any[] = []
-    function destroy(vnode) {
-      result.push(vnode)
-    }
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', {}, [
-        new VNode('span', { hook: { destroy } }, undefined, 'child 1'),
-        new VNode('span', {}, undefined, 'child 2')
-      ])
-    ])
-    const vnode2 = new VNode('div')
-    patch(vnode0, vnode1)
-    patch(vnode1, vnode2)
-    expect(result.length).toBe(1)
-  })
-
-  it('should handle text vnodes with `undefined` `data` property', () => {
-    const vnode1 = new VNode('div', {}, [createTextVNode(' ')])
-    const vnode2 = new VNode('div', {}, [])
-    patch(vnode0, vnode1)
-    patch(vnode1, vnode2)
-  })
-
-  it('should invoke `destroy` module hook for all removed children', () => {
-    let created = 0
-    let destroyed = 0
-    const patch1 = createPatchFunction({
-      nodeOps,
-      modules: modules.concat([
-        {
-          create() {
-            created++
-          }
-        },
-        {
-          destroy() {
-            destroyed++
-          }
-        }
-      ])
-    })
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', {}, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode('span', {}, undefined, 'child 2')
-      ])
-    ])
-    const vnode2 = new VNode('div', {})
-    patch1(vnode0, vnode1)
-    expect(destroyed).toBe(1) // should invoke for replaced root nodes too
-    patch1(vnode1, vnode2)
-    expect(created).toBe(5)
-    expect(destroyed).toBe(5)
-  })
-
-  it('should not invoke `create` and `remove` module hook for text nodes', () => {
-    let created = 0
-    let removed = 0
-    const patch1 = createPatchFunction({
-      nodeOps,
-      modules: modules.concat([
-        {
-          create() {
-            created++
-          }
-        },
-        {
-          remove() {
-            removed++
-          }
-        }
-      ])
-    })
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first child'),
-      createTextVNode(''),
-      new VNode('span', {}, undefined, 'third child')
-    ])
-    const vnode2 = new VNode('div', {})
-    patch1(vnode0, vnode1)
-    patch1(vnode1, vnode2)
-    expect(created).toBe(3)
-    expect(removed).toBe(2)
-  })
-
-  it('should not invoke `destroy` module hook for text nodes', () => {
-    let created = 0
-    let destroyed = 0
-    const patch1 = createPatchFunction({
-      nodeOps,
-      modules: modules.concat([
-        {
-          create() {
-            created++
-          }
-        },
-        {
-          destroy() {
-            destroyed++
-          }
-        }
-      ])
-    })
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', {}, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode('span', {}, [
-          createTextVNode('text1'),
-          createTextVNode('text2')
-        ])
-      ])
-    ])
-    const vnode2 = new VNode('div', {})
-    patch1(vnode0, vnode1)
-    expect(destroyed).toBe(1) // should invoke for replaced root nodes too
-    patch1(vnode1, vnode2)
-    expect(created).toBe(5)
-    expect(destroyed).toBe(5)
-  })
-
-  it('should call `create` listener before inserted into parent but after children', () => {
-    const result: any[] = []
-    function create(empty, vnode) {
-      expect(vnode.elm.children.length).toBe(2)
-      expect(vnode.elm.parentNode).toBe(null)
-      result.push(vnode)
-    }
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}, undefined, 'first sibling'),
-      new VNode('div', { hook: { create } }, [
-        new VNode('span', {}, undefined, 'child 1'),
-        new VNode('span', {}, undefined, 'child 2')
-      ]),
-      new VNode('span', {}, undefined, "can't touch me")
-    ])
-    patch(vnode0, vnode1)
-    expect(result.length).toBe(1)
-  })
-})
diff --git a/test/unit/modules/vdom/patch/hydration.spec.ts b/test/unit/modules/vdom/patch/hydration.spec.ts
deleted file mode 100644
index a757226f83d..00000000000
--- a/test/unit/modules/vdom/patch/hydration.spec.ts
+++ /dev/null
@@ -1,413 +0,0 @@
-import Vue from 'vue'
-import VNode from 'core/vdom/vnode'
-import { patch } from 'web/runtime/patch'
-import { SSR_ATTR } from 'shared/constants'
-
-function createMockSSRDOM(innerHTML) {
-  const dom = document.createElement('div')
-  dom.setAttribute(SSR_ATTR, 'true')
-  dom.innerHTML = innerHTML
-  return dom
-}
-
-describe('vdom patch: hydration', () => {
-  let vnode0
-  beforeEach(() => {
-    vnode0 = new VNode('p', { attrs: { id: '1' } }, [
-      createTextVNode('hello world')
-    ])
-    patch(null, vnode0)
-  })
-
-  it('should hydrate elements when server-rendered DOM tree is same as virtual DOM tree', () => {
-    const result: any[] = []
-    function init(vnode) {
-      result.push(vnode)
-    }
-    function createServerRenderedDOM() {
-      const root = document.createElement('div')
-      root.setAttribute(SSR_ATTR, 'true')
-      const span = document.createElement('span')
-      root.appendChild(span)
-      const div = document.createElement('div')
-      const child1 = document.createElement('span')
-      const child2 = document.createElement('span')
-      child1.textContent = 'hi'
-      child2.textContent = 'ho'
-      div.appendChild(child1)
-      div.appendChild(child2)
-      root.appendChild(div)
-      return root
-    }
-    const node0 = createServerRenderedDOM()
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}),
-      new VNode('div', { hook: { init } }, [
-        new VNode('span', {}, [
-          new VNode(undefined, undefined, undefined, 'hi')
-        ]),
-        new VNode('span', {}, [
-          new VNode(undefined, undefined, undefined, 'ho')
-        ])
-      ])
-    ])
-    patch(node0, vnode1)
-    expect(result.length).toBe(1)
-
-    function traverseAndAssert(vnode, element) {
-      expect(vnode.elm).toBe(element)
-      if (vnode.children) {
-        vnode.children.forEach((node, i) => {
-          traverseAndAssert(node, element.childNodes[i])
-        })
-      }
-    }
-    // ensure vnodes are correctly associated with actual DOM
-    traverseAndAssert(vnode1, node0)
-
-    // check update
-    const vnode2 = new VNode('div', { attrs: { id: 'foo' } }, [
-      new VNode('span', { attrs: { id: 'bar' } }),
-      new VNode('div', { hook: { init } }, [
-        new VNode('span', {}),
-        new VNode('span', {})
-      ])
-    ])
-    patch(vnode1, vnode2)
-    expect(node0.id).toBe('foo')
-    expect(node0.children[0].id).toBe('bar')
-  })
-
-  it('should warn message that virtual DOM tree is not matching when hydrate element', () => {
-    function createServerRenderedDOM() {
-      const root = document.createElement('div')
-      root.setAttribute(SSR_ATTR, 'true')
-      const span = document.createElement('span')
-      root.appendChild(span)
-      const div = document.createElement('div')
-      const child1 = document.createElement('span')
-      div.appendChild(child1)
-      root.appendChild(div)
-      return root
-    }
-    const node0 = createServerRenderedDOM()
-    const vnode1 = new VNode('div', {}, [
-      new VNode('span', {}),
-      new VNode('div', {}, [new VNode('span', {}), new VNode('span', {})])
-    ])
-    patch(node0, vnode1)
-    expect(
-      'The client-side rendered virtual DOM tree is not matching'
-    ).toHaveBeenWarned()
-  })
-
-  // component hydration is better off with a more e2e approach
-  it('should hydrate components when server-rendered DOM tree is same as virtual DOM tree', done => {
-    const dom = createMockSSRDOM(
-      '<span>foo</span><div class="b a"><span>foo qux</span></div><!---->'
-    )
-    const originalNode1 = dom.children[0]
-    const originalNode2 = dom.children[1]
-
-    const vm = new Vue({
-      template:
-        '<div><span>{{msg}}</span><test class="a" :msg="msg"></test><p v-if="ok"></p></div>',
-      data: {
-        msg: 'foo',
-        ok: false
-      },
-      components: {
-        test: {
-          props: ['msg'],
-          data() {
-            return { a: 'qux' }
-          },
-          template: '<div class="b"><span>{{msg}} {{a}}</span></div>'
-        }
-      }
-    })
-
-    expect(() => {
-      vm.$mount(dom)
-    }).not.toThrow()
-    expect('not matching server-rendered content').not.toHaveBeenWarned()
-    expect(vm.$el).toBe(dom)
-    expect(vm.$children[0].$el).toBe(originalNode2)
-    expect(vm.$el.children[0]).toBe(originalNode1)
-    expect(vm.$el.children[1]).toBe(originalNode2)
-    vm.msg = 'bar'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe(
-        '<span>bar</span><div class="b a"><span>bar qux</span></div><!---->'
-      )
-      vm.$children[0].a = 'ququx'
-    })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>bar</span><div class="b a"><span>bar ququx</span></div><!---->'
-        )
-        vm.ok = true
-      })
-      .then(() => {
-        expect(vm.$el.innerHTML).toBe(
-          '<span>bar</span><div class="b a"><span>bar ququx</span></div><p></p>'
-        )
-      })
-      .then(done)
-  })
-
-  it('should warn failed hydration for non-matching DOM in child component', () => {
-    const dom = createMockSSRDOM('<div><span></span></div>')
-
-    new Vue({
-      template: '<div><test></test></div>',
-      components: {
-        test: {
-          template: '<div><a></a></div>'
-        }
-      }
-    }).$mount(dom)
-
-    expect('not matching server-rendered content').toHaveBeenWarned()
-  })
-
-  it('should warn failed hydration when component is not properly registered', () => {
-    const dom = createMockSSRDOM('<div><foo></foo></div>')
-
-    new Vue({
-      template: '<div><foo></foo></div>'
-    }).$mount(dom)
-
-    expect('not matching server-rendered content').toHaveBeenWarned()
-    expect('Unknown custom element: <foo>').toHaveBeenWarned()
-  })
-
-  it('should overwrite textNodes in the correct position but with mismatching text without warning', () => {
-    const dom = createMockSSRDOM('<div><span>foo</span></div>')
-
-    new Vue({
-      template: '<div><test></test></div>',
-      components: {
-        test: {
-          data() {
-            return { a: 'qux' }
-          },
-          template: '<div><span>{{a}}</span></div>'
-        }
-      }
-    }).$mount(dom)
-
-    expect('not matching server-rendered content').not.toHaveBeenWarned()
-    expect(dom.querySelector('span').textContent).toBe('qux')
-  })
-
-  it('should pick up elements with no children and populate without warning', done => {
-    const dom = createMockSSRDOM('<div><span></span></div>')
-    const span = dom.querySelector('span')
-
-    const vm = new Vue({
-      template: '<div><test></test></div>',
-      components: {
-        test: {
-          data() {
-            return { a: 'qux' }
-          },
-          template: '<div><span>{{a}}</span></div>'
-        }
-      }
-    }).$mount(dom)
-
-    expect('not matching server-rendered content').not.toHaveBeenWarned()
-    expect(span).toBe(vm.$el.querySelector('span'))
-    expect(vm.$el.innerHTML).toBe('<div><span>qux</span></div>')
-
-    vm.$children[0].a = 'foo'
-    waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<div><span>foo</span></div>')
-    }).then(done)
-  })
-
-  it('should hydrate async component', done => {
-    const dom = createMockSSRDOM('<span>foo</span>')
-    const span = dom.querySelector('span')
-
-    const Foo = resolve =>
-      setTimeout(() => {
-        resolve({
-          data: () => ({ msg: 'foo' }),
-          template: `<span>{{ msg }}</span>`
-        })
-      }, 0)
-
-    const vm = new Vue({
-      template: '<div><foo ref="foo" /></div>',
-      components: { Foo }
-    }).$mount(dom)
-
-    expect('not matching server-rendered content').not.toHaveBeenWarned()
-    expect(dom.innerHTML).toBe('<span>foo</span>')
-    expect(vm.$refs.foo).toBeUndefined()
-
-    setTimeout(() => {
-      expect(dom.innerHTML).toBe('<span>foo</span>')
-      expect(vm.$refs.foo).not.toBeUndefined()
-      vm.$refs.foo.msg = 'bar'
-      waitForUpdate(() => {
-        expect(dom.innerHTML).toBe('<span>bar</span>')
-        expect(dom.querySelector('span')).toBe(span)
-      }).then(done)
-    }, 50)
-  })
-
-  it('should hydrate async component without showing loading', done => {
-    const dom = createMockSSRDOM('<span>foo</span>')
-    const span = dom.querySelector('span')
-
-    const Foo = () => ({
-      component: new Promise(resolve => {
-        setTimeout(() => {
-          resolve({
-            data: () => ({ msg: 'foo' }),
-            template: `<span>{{ msg }}</span>`
-          })
-        }, 10)
-      }),
-      delay: 1,
-      loading: {
-        render: h => h('span', 'loading')
-      }
-    })
-
-    const vm = new Vue({
-      template: '<div><foo ref="foo" /></div>',
-      components: { Foo }
-    }).$mount(dom)
-
-    expect('not matching server-rendered content').not.toHaveBeenWarned()
-    expect(dom.innerHTML).toBe('<span>foo</span>')
-    expect(vm.$refs.foo).toBeUndefined()
-
-    setTimeout(() => {
-      expect(dom.innerHTML).toBe('<span>foo</span>')
-    }, 2)
-
-    setTimeout(() => {
-      expect(dom.innerHTML).toBe('<span>foo</span>')
-      expect(vm.$refs.foo).not.toBeUndefined()
-      vm.$refs.foo.msg = 'bar'
-      waitForUpdate(() => {
-        expect(dom.innerHTML).toBe('<span>bar</span>')
-        expect(dom.querySelector('span')).toBe(span)
-      }).then(done)
-    }, 50)
-  })
-
-  it('should hydrate async component by replacing DOM if error occurs', done => {
-    const dom = createMockSSRDOM('<span>foo</span>')
-
-    const Foo = () => ({
-      component: new Promise((resolve, reject) => {
-        setTimeout(() => {
-          reject('something went wrong')
-        }, 10)
-      }),
-      error: {
-        render: h => h('span', 'error')
-      }
-    })
-
-    new Vue({
-      template: '<div><foo ref="foo" /></div>',
-      components: { Foo }
-    }).$mount(dom)
-
-    expect('not matching server-rendered content').not.toHaveBeenWarned()
-    expect(dom.innerHTML).toBe('<span>foo</span>')
-
-    setTimeout(() => {
-      expect('Failed to resolve async').toHaveBeenWarned()
-      expect(dom.innerHTML).toBe('<span>error</span>')
-      done()
-    }, 50)
-  })
-
-  it('should hydrate v-html with children', () => {
-    const dom = createMockSSRDOM('<span>foo</span>')
-
-    new Vue({
-      data: {
-        html: `<span>foo</span>`
-      },
-      template: `<div v-html="html">hello</div>`
-    }).$mount(dom)
-
-    expect('not matching server-rendered content').not.toHaveBeenWarned()
-  })
-
-  it('should warn mismatching v-html', () => {
-    const dom = createMockSSRDOM('<span>bar</span>')
-
-    new Vue({
-      data: {
-        html: `<span>foo</span>`
-      },
-      template: `<div v-html="html">hello</div>`
-    }).$mount(dom)
-
-    expect('not matching server-rendered content').toHaveBeenWarned()
-  })
-
-  it('should hydrate with adjacent text nodes from array children (e.g. slots)', () => {
-    const dom = createMockSSRDOM('<div>foo</div> hello')
-
-    new Vue({
-      template: `<test>hello</test>`,
-      components: {
-        test: {
-          template: `
-            <div>
-              <div>foo</div>
-              <slot/>
-            </div>
-          `
-        }
-      }
-    }).$mount(dom)
-    expect('not matching server-rendered content').not.toHaveBeenWarned()
-  })
-
-  // #7063
-  it('should properly initialize dynamic style bindings for future updates', done => {
-    const dom = createMockSSRDOM('<div style="padding-left:0px"></div>')
-
-    const vm = new Vue({
-      data: {
-        style: { paddingLeft: '0px' }
-      },
-      template: `<div><div :style="style"></div></div>`
-    }).$mount(dom)
-
-    // should update
-    vm.style.paddingLeft = '100px'
-    waitForUpdate(() => {
-      expect(dom.children[0].style.paddingLeft).toBe('100px')
-    }).then(done)
-  })
-
-  it('should properly initialize dynamic class bindings for future updates', done => {
-    const dom = createMockSSRDOM('<div class="foo bar"></div>')
-
-    const vm = new Vue({
-      data: {
-        cls: [{ foo: true }, 'bar']
-      },
-      template: `<div><div :class="cls"></div></div>`
-    }).$mount(dom)
-
-    // should update
-    vm.cls[0].foo = false
-    waitForUpdate(() => {
-      expect(dom.children[0].className).toBe('bar')
-    }).then(done)
-  })
-})
diff --git a/test/unit/specs/api/data_spec.js b/test/unit/specs/api/data_spec.js
new file mode 100644
index 00000000000..bcc3eea2e21
--- /dev/null
+++ b/test/unit/specs/api/data_spec.js
@@ -0,0 +1,171 @@
+var Vue = require('src')
+var _ = require('src/util')
+var nextTick = _.nextTick
+
+describe('Data API', function () {
+  var vm
+  beforeEach(function () {
+    var el = document.createElement('div')
+    el.setAttribute('prop', 'foo')
+    vm = new Vue({
+      el: el,
+      props: ['prop'],
+      data: {
+        a: 1,
+        b: {
+          c: 2
+        }
+      },
+      filters: {
+        double: function (v) {
+          return v * 2
+        }
+      },
+      computed: {
+        d: function () {
+          return this.a + 1
+        }
+      }
+    })
+  })
+
+  it('$get', function () {
+    expect(vm.$get('a')).toBe(1)
+    expect(vm.$get('b["c"]')).toBe(2)
+    expect(vm.$get('a + b.c')).toBe(3)
+    expect(vm.$get('c')).toBeUndefined()
+    // invalid, should warn
+    vm.$get('a(')
+    expect('Invalid expression').toHaveBeenWarned()
+  })
+
+  it('$set', function () {
+    vm.$set('a', 2)
+    expect(vm.a).toBe(2)
+    vm.$set('b["c"]', 3)
+    expect(vm.b.c).toBe(3)
+    // setting unexisting
+    vm.$set('c.d', 2)
+    expect(vm.c.d).toBe(2)
+    // warn against setting unexisting
+    expect('Consider pre-initializing').toHaveBeenWarned()
+  })
+
+  it('$set invalid', function () {
+    vm.$set('c + d', 1)
+    expect('Invalid setter expression').toHaveBeenWarned()
+  })
+
+  it('$delete', function () {
+    vm._digest = jasmine.createSpy()
+    vm.$delete('a')
+    expect(_.hasOwn(vm, 'a')).toBe(false)
+    expect(_.hasOwn(vm._data, 'a')).toBe(false)
+    expect(vm._digest).toHaveBeenCalled()
+    // reserved key should not be deleted
+    vm.$delete('_data')
+    expect(vm._data).toBeTruthy()
+  })
+
+  it('$watch', function (done) {
+    var spy = jasmine.createSpy()
+    // test immediate invoke
+    var unwatch = vm.$watch('a + b.c', spy, {
+      immediate: true
+    })
+    expect(spy).toHaveBeenCalledWith(3)
+    vm.a = 2
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(4, 3)
+      // unwatch
+      unwatch()
+      vm.a = 3
+      nextTick(function () {
+        expect(spy.calls.count()).toBe(2)
+        done()
+      })
+    })
+  })
+
+  it('function $watch', function (done) {
+    var spy = jasmine.createSpy()
+    // test immediate invoke
+    var unwatch = vm.$watch(function () {
+      return this.a + this.b.c
+    }, spy, { immediate: true })
+    expect(spy).toHaveBeenCalledWith(3)
+    vm.a = 2
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(4, 3)
+      // unwatch
+      unwatch()
+      vm.a = 3
+      nextTick(function () {
+        expect(spy.calls.count()).toBe(2)
+        done()
+      })
+    })
+  })
+
+  it('deep $watch', function (done) {
+    var oldB = vm.b
+    var spy = jasmine.createSpy()
+    vm.$watch('b', spy, {
+      deep: true
+    })
+    vm.b.c = 3
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(oldB, oldB)
+      vm.b = { c: 4 }
+      nextTick(function () {
+        expect(spy).toHaveBeenCalledWith(vm.b, oldB)
+        done()
+      })
+    })
+  })
+
+  it('$watch with filters', function (done) {
+    var spy = jasmine.createSpy()
+    vm.$watch('a | double', spy)
+    vm.a = 2
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(4, 2)
+      done()
+    })
+  })
+
+  it('$eval', function () {
+    expect(vm.$eval('a')).toBe(1)
+    expect(vm.$eval('b.c')).toBe(2)
+    expect(vm.$eval('a + b.c | double')).toBe(6)
+  })
+
+  it('$interpolate', function () {
+    expect(vm.$interpolate('abc')).toBe('abc')
+    expect(vm.$interpolate('{{a}}')).toBe('1')
+    expect(vm.$interpolate('{{a}} and {{a + b.c | double}}')).toBe('1 and 6')
+  })
+
+  if (typeof console !== 'undefined') {
+    it('$log', function () {
+      var oldLog = console.log
+      var spy = jasmine.createSpy()
+      console.log = function (val) {
+        expect(val.a).toBe(1)
+        expect(val.b.c).toBe(2)
+        expect(val.d).toBe(2)
+        expect(val.prop).toBe('foo')
+        spy()
+      }
+      vm.$log()
+      expect(spy.calls.count()).toBe(1)
+      console.log = function (val) {
+        expect(val.c).toBe(2)
+        spy()
+      }
+      vm.$log('b')
+      expect(spy.calls.count()).toBe(2)
+      console.log = oldLog
+    })
+  }
+})
diff --git a/test/unit/specs/api/dom_spec.js b/test/unit/specs/api/dom_spec.js
new file mode 100644
index 00000000000..cadab8a7a07
--- /dev/null
+++ b/test/unit/specs/api/dom_spec.js
@@ -0,0 +1,190 @@
+/**
+* We are not testing transition-related stuff here,
+* those are tested in transition_spec.js.
+*/
+
+var Vue = require('src')
+var _ = require('src/util')
+
+describe('DOM API', function () {
+  var vm, vm2, parent, target, sibling, empty, spy
+  beforeEach(function () {
+    spy = jasmine.createSpy('dom')
+    parent = document.createElement('div')
+    target = document.createElement('div')
+    sibling = document.createElement('div')
+    empty = document.createElement('div')
+    parent.appendChild(target)
+    parent.appendChild(sibling)
+    var el = document.createElement('div')
+    vm = new Vue({ el: el })
+    // fragment instance
+    var frag = document.createDocumentFragment()
+    frag.appendChild(document.createElement('p'))
+    frag.appendChild(document.createElement('span'))
+    vm2 = new Vue({
+      el: frag
+    })
+  })
+
+  describe('$appendTo', function () {
+    it('normal instance', function () {
+      vm.$appendTo(parent, spy)
+      expect(parent.childNodes.length).toBe(3)
+      expect(parent.lastChild).toBe(vm.$el)
+      expect(spy.calls.count()).toBe(1)
+    })
+
+    it('fragment instance', function () {
+      vm2.$appendTo(parent, spy)
+      expect(parent.childNodes.length).toBe(6)
+      expect(parent.childNodes[2]).toBe(vm2._fragmentStart)
+      expect(parent.childNodes[2]).toBe(vm2.$el)
+      expect(parent.childNodes[3].tagName).toBe('P')
+      expect(parent.childNodes[4].tagName).toBe('SPAN')
+      expect(parent.childNodes[5]).toBe(vm2._fragmentEnd)
+      expect(spy.calls.count()).toBe(1)
+    })
+  })
+
+  describe('$prependTo', function () {
+    it('normal instance', function () {
+      vm.$prependTo(parent, spy)
+      expect(parent.childNodes.length).toBe(3)
+      expect(parent.firstChild).toBe(vm.$el)
+      expect(spy.calls.count()).toBe(1)
+      vm.$prependTo(empty, spy)
+      expect(empty.childNodes.length).toBe(1)
+      expect(empty.firstChild).toBe(vm.$el)
+      expect(spy.calls.count()).toBe(2)
+    })
+
+    it('fragment instance', function () {
+      vm2.$prependTo(parent, spy)
+      expect(parent.childNodes.length).toBe(6)
+      expect(parent.childNodes[0]).toBe(vm2._fragmentStart)
+      expect(parent.childNodes[0]).toBe(vm2.$el)
+      expect(parent.childNodes[1].tagName).toBe('P')
+      expect(parent.childNodes[2].tagName).toBe('SPAN')
+      expect(parent.childNodes[3]).toBe(vm2._fragmentEnd)
+      expect(spy.calls.count()).toBe(1)
+      // empty
+      vm2.$prependTo(empty, spy)
+      expect(empty.childNodes.length).toBe(4)
+      expect(empty.childNodes[0]).toBe(vm2._fragmentStart)
+      expect(empty.childNodes[0]).toBe(vm2.$el)
+      expect(empty.childNodes[1].tagName).toBe('P')
+      expect(empty.childNodes[2].tagName).toBe('SPAN')
+      expect(empty.childNodes[3]).toBe(vm2._fragmentEnd)
+      expect(spy.calls.count()).toBe(2)
+    })
+  })
+
+  describe('$before', function () {
+    it('normal instance', function () {
+      vm.$before(sibling, spy)
+      expect(parent.childNodes.length).toBe(3)
+      expect(parent.childNodes[1]).toBe(vm.$el)
+      expect(spy.calls.count()).toBe(1)
+    })
+
+    it('fragment instance', function () {
+      vm2.$before(sibling, spy)
+      expect(parent.childNodes.length).toBe(6)
+      expect(parent.childNodes[1]).toBe(vm2._fragmentStart)
+      expect(parent.childNodes[1]).toBe(vm2.$el)
+      expect(parent.childNodes[2].tagName).toBe('P')
+      expect(parent.childNodes[3].tagName).toBe('SPAN')
+      expect(parent.childNodes[4]).toBe(vm2._fragmentEnd)
+      expect(spy.calls.count()).toBe(1)
+    })
+  })
+
+  describe('$after', function () {
+    it('normal instance', function () {
+      vm.$after(target, spy)
+      expect(parent.childNodes.length).toBe(3)
+      expect(parent.childNodes[1]).toBe(vm.$el)
+      expect(spy.calls.count()).toBe(1)
+    })
+
+    it('normal instance no next sibling', function () {
+      vm.$after(sibling, spy)
+      expect(parent.childNodes.length).toBe(3)
+      expect(parent.lastChild).toBe(vm.$el)
+      expect(spy.calls.count()).toBe(1)
+    })
+
+    it('fragment instance', function () {
+      vm2.$after(target, spy)
+      expect(parent.childNodes.length).toBe(6)
+      expect(parent.childNodes[1]).toBe(vm2._fragmentStart)
+      expect(parent.childNodes[1]).toBe(vm2.$el)
+      expect(parent.childNodes[2].tagName).toBe('P')
+      expect(parent.childNodes[3].tagName).toBe('SPAN')
+      expect(parent.childNodes[4]).toBe(vm2._fragmentEnd)
+      expect(spy.calls.count()).toBe(1)
+    })
+
+    it('fragment instance no next sibling', function () {
+      vm2.$after(sibling, spy)
+      expect(parent.childNodes.length).toBe(6)
+      expect(parent.childNodes[2]).toBe(vm2._fragmentStart)
+      expect(parent.childNodes[2]).toBe(vm2.$el)
+      expect(parent.childNodes[3].tagName).toBe('P')
+      expect(parent.childNodes[4].tagName).toBe('SPAN')
+      expect(parent.childNodes[5]).toBe(vm2._fragmentEnd)
+      expect(spy.calls.count()).toBe(1)
+    })
+  })
+
+  describe('$remove', function () {
+    it('normal instance', function () {
+      vm.$before(sibling)
+      expect(parent.childNodes.length).toBe(3)
+      expect(parent.childNodes[1]).toBe(vm.$el)
+      vm.$remove(spy)
+      expect(parent.childNodes.length).toBe(2)
+      expect(parent.childNodes[0]).toBe(target)
+      expect(parent.childNodes[1]).toBe(sibling)
+      expect(spy.calls.count()).toBe(1)
+    })
+
+    it('fragment instance', function () {
+      vm2.$before(sibling)
+      expect(parent.childNodes.length).toBe(6)
+      expect(parent.childNodes[1]).toBe(vm2._fragmentStart)
+      expect(parent.childNodes[1]).toBe(vm2.$el)
+      expect(parent.childNodes[2].tagName).toBe('P')
+      expect(parent.childNodes[3].tagName).toBe('SPAN')
+      expect(parent.childNodes[4]).toBe(vm2._fragmentEnd)
+      vm2.$remove(spy)
+      expect(parent.childNodes.length).toBe(2)
+      expect(parent.childNodes[0]).toBe(target)
+      expect(parent.childNodes[1]).toBe(sibling)
+      expect(spy.calls.count()).toBe(1)
+    })
+
+    it('detached', function () {
+      vm.$remove(spy)
+      expect(spy.calls.count()).toBe(1)
+    })
+  })
+
+  describe('$nextTick', function () {
+    it('should work', function (done) {
+      var context
+      var called = false
+      vm.$nextTick(function () {
+        called = true
+        context = this
+      })
+      expect(called).toBe(false)
+      _.nextTick(function () {
+        expect(called).toBe(true)
+        expect(context).toBe(vm)
+        done()
+      })
+    })
+  })
+})
diff --git a/test/unit/specs/api/events_spec.js b/test/unit/specs/api/events_spec.js
new file mode 100644
index 00000000000..2b0ea1ded49
--- /dev/null
+++ b/test/unit/specs/api/events_spec.js
@@ -0,0 +1,234 @@
+var Vue = require('src')
+
+describe('Events API', function () {
+  var vm, spy
+  beforeEach(function () {
+    vm = new Vue()
+    spy = jasmine.createSpy('emitter')
+  })
+
+  it('$on', function () {
+    vm.$on('test', function () {
+      // expect correct context
+      expect(this).toBe(vm)
+      spy.apply(this, arguments)
+    })
+    vm.$emit('test', 1, 2, 3, 4)
+    expect(spy.calls.count()).toBe(1)
+    expect(spy).toHaveBeenCalledWith(1, 2, 3, 4)
+  })
+
+  it('$once', function () {
+    vm.$once('test', spy)
+    vm.$emit('test', 1, 2, 3)
+    vm.$emit('test', 2, 3, 4)
+    expect(spy.calls.count()).toBe(1)
+    expect(spy).toHaveBeenCalledWith(1, 2, 3)
+  })
+
+  it('$off', function () {
+    vm.$on('test1', spy)
+    vm.$on('test2', spy)
+    vm.$off()
+    vm.$emit('test1')
+    vm.$emit('test2')
+    expect(spy).not.toHaveBeenCalled()
+  })
+
+  it('$off event', function () {
+    vm.$on('test1', spy)
+    vm.$on('test2', spy)
+    vm.$off('test1')
+    vm.$off('test1') // test off something that's already off
+    vm.$emit('test1', 1)
+    vm.$emit('test2', 2)
+    expect(spy.calls.count()).toBe(1)
+    expect(spy).toHaveBeenCalledWith(2)
+  })
+
+  it('$off event + fn', function () {
+    var spy2 = jasmine.createSpy('emitter')
+    vm.$on('test', spy)
+    vm.$on('test', spy2)
+    vm.$off('test', spy)
+    vm.$emit('test', 1, 2, 3)
+    expect(spy).not.toHaveBeenCalled()
+    expect(spy2.calls.count()).toBe(1)
+    expect(spy2).toHaveBeenCalledWith(1, 2, 3)
+  })
+
+  it('$broadcast', function () {
+    var child1 = new Vue({ parent: vm })
+    var child2 = new Vue({ parent: vm })
+    var child3 = new Vue({ parent: child1 })
+    child1.$on('test', spy)
+    child2.$on('test', spy)
+    child3.$on('test', spy)
+    vm.$broadcast('test')
+    expect(spy.calls.count()).toBe(2) // should not propagate by default
+  })
+
+  it('$broadcast with propagation', function () {
+    var child1 = new Vue({ parent: vm })
+    var child2 = new Vue({ parent: vm })
+    var child3 = new Vue({ parent: child1 })
+    child1.$on('test', function () {
+      spy()
+      return true
+    })
+    child2.$on('test', spy)
+    child3.$on('test', spy)
+    vm.$broadcast('test')
+    expect(spy.calls.count()).toBe(3)
+  })
+
+  it('$broadcast optimization', function () {
+    var child = new Vue({ parent: vm })
+    var child2 = new Vue({ parent: child })
+    // hooks should not incurr the bookkeeping cost
+    child.$on('hook:created', function () {})
+    expect(vm._eventsCount['hook:created']).toBeUndefined()
+
+    function handler () {
+      spy()
+      return true
+    }
+
+    child.$on('test', handler)
+    expect(vm._eventsCount['test']).toBe(1)
+    // child2's $emit & $broadcast
+    // shouldn't get called if no child listens to the event
+    child2.$emit = spy
+    child2.$broadcast = spy
+    vm.$broadcast('test')
+    expect(spy.calls.count()).toBe(1)
+    // check $off bookkeeping
+    child.$off('test', handler)
+    expect(vm._eventsCount['test']).toBe(0)
+    function noop () {}
+    child.$on('test', noop)
+    child2.$on('test', noop)
+    expect(vm._eventsCount['test']).toBe(2)
+    child.$off('test')
+    expect(vm._eventsCount['test']).toBe(1)
+    child.$on('test', noop)
+    child2.$on('test', noop)
+    expect(vm._eventsCount['test']).toBe(3)
+    child.$off()
+    child2.$off()
+    expect(vm._eventsCount['test']).toBe(0)
+  })
+
+  it('$broadcast cancel', function () {
+    var child = new Vue({ parent: vm })
+    var child2 = new Vue({ parent: child })
+    child.$on('test', function () {
+      return false
+    })
+    child2.$on('test', spy)
+    vm.$broadcast('test')
+    expect(spy).not.toHaveBeenCalled()
+  })
+
+  it('$dispatch', function () {
+    var child = new Vue({ parent: vm })
+    var child2 = new Vue({ parent: child })
+    child2.$on('test', spy)
+    child.$on('test', spy)
+    vm.$on('test', spy)
+    child2.$dispatch('test')
+    expect(spy.calls.count()).toBe(2) // should trigger on self, but not propagate to root
+  })
+
+  it('$dispatch with propagation', function () {
+    var child = new Vue({ parent: vm })
+    var child2 = new Vue({ parent: child })
+    var child3 = new Vue({ parent: child2 })
+    child.$on('test', function () {
+      spy()
+      return true
+    })
+    vm.$on('test', spy)
+    child3.$dispatch('test')
+    expect(spy.calls.count()).toBe(2)
+  })
+
+  it('handle $dispatch by v-on inline-statement', function () {
+    var parent = new Vue({
+      el: document.createElement('div'),
+      template: '<child1 @test="onTest()" v-ref:child></child1>',
+      methods: {
+        onTest: function () {
+          spy()
+        }
+      },
+      components: {
+        child1: {
+          template: '<child2 @test="onTest" v-ref:child></child2>',
+          methods: {
+            onTest: function () {
+              spy()
+            }
+          },
+          components: {
+            child2: {
+              template: '<child3 @test="onTest()" v-ref:child></child3>',
+              methods: {
+                onTest: function () {
+                  spy()
+                  return true
+                }
+              },
+              components: {
+                child3: {
+                  template: '<child4 @test="onTest" v-ref:child></child4>',
+                  methods: {
+                    onTest: function () {
+                      spy()
+                      return true
+                    }
+                  },
+                  components: {
+                    child4: {}
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    })
+    parent
+      .$refs.child // child1
+      .$refs.child // child2
+      .$refs.child // child3
+      .$refs.child // child4
+      .$dispatch('test')
+    expect(spy.calls.count()).toBe(3)
+  })
+
+  it('$dispatch forward on immediate inline component handler', function () {
+    var shouldPropagate = true
+    var parent = new Vue({
+      el: document.createElement('div'),
+      template: '<child @test="onTest" v-ref:child></child>',
+      events: {
+        test: spy
+      },
+      methods: {
+        onTest: function () {
+          spy()
+          return shouldPropagate
+        }
+      },
+      components: {
+        child: {}
+      }
+    })
+    parent.$refs.child.$dispatch('test')
+    expect(spy.calls.count()).toBe(2)
+    shouldPropagate = false
+    parent.$refs.child.$dispatch('test')
+    expect(spy.calls.count()).toBe(3)
+  })
+})
diff --git a/test/unit/specs/api/lifecycle_spec.js b/test/unit/specs/api/lifecycle_spec.js
new file mode 100644
index 00000000000..5a2db3976d4
--- /dev/null
+++ b/test/unit/specs/api/lifecycle_spec.js
@@ -0,0 +1,322 @@
+var Vue = require('src')
+var compiler = require('src/compiler')
+
+describe('Lifecycle API', function () {
+  describe('$mount', function () {
+    var el, frag
+    beforeEach(function () {
+      el = document.createElement('div')
+      el.textContent = '{{test}}'
+      frag = document.createDocumentFragment()
+      frag.appendChild(el)
+    })
+
+    it('normal', function () {
+      var vm = new Vue({
+        data: {
+          test: 'foo'
+        }
+      })
+      vm.$mount(el)
+      expect(vm.$el).toBe(el)
+      expect(el.__vue__).toBe(vm)
+      expect(el.textContent).toBe('foo')
+    })
+
+    it('auto-create', function () {
+      var vm = new Vue({
+        template: '{{a}}',
+        data: {
+          a: 123
+        }
+      })
+      vm.$mount()
+      expect(vm.$el).toBeTruthy()
+      expect(vm.$el.tagName).toBe('DIV')
+      expect(vm.$el.textContent).toBe('123')
+    })
+
+    it('selector', function () {
+      el.id = 'mount-test'
+      document.body.appendChild(el)
+      var vm = new Vue({
+        data: { test: 'foo' }
+      })
+      vm.$mount('#mount-test')
+      expect(vm.$el).toBe(el)
+      expect(el.__vue__).toBe(vm)
+      expect(el.textContent).toBe('foo')
+      document.body.removeChild(el)
+    })
+
+    it('warn invalid selector', function () {
+      var vm = new Vue()
+      vm.$mount('#none-exist')
+      expect('Cannot find element').toHaveBeenWarned()
+    })
+
+    it('replace', function () {
+      el.className = 'replace-test'
+      document.body.appendChild(el)
+      var vm = new Vue({
+        replace: true,
+        data: { test: 'foo' },
+        template: '<div>{{test}}</div>'
+      })
+      vm.$mount(el)
+      expect(vm.$el).not.toBe(el)
+      expect(vm.$el.textContent).toBe('foo')
+      expect(document.body.contains(el)).toBe(false)
+      expect(document.body.lastChild).toBe(vm.$el)
+      expect(vm.$el.className).toBe('replace-test')
+      document.body.removeChild(vm.$el)
+    })
+
+    it('precompiled linker', function () {
+      var linker = compiler.compile(el, Vue.options)
+      var vm = new Vue({
+        _linker: linker,
+        data: {
+          test: 'foo'
+        }
+      })
+      vm.$mount(el)
+      expect(vm.$el).toBe(el)
+      expect(el.__vue__).toBe(vm)
+      expect(el.textContent).toBe('foo')
+    })
+
+    it('mount to fragment', function () {
+      var vm = new Vue({
+        data: { test: 'frag' }
+      })
+      vm.$mount(frag)
+      expect(vm._fragment).toBe(frag)
+      expect(vm.$el.nextSibling.textContent).toBe('frag')
+    })
+
+    it('replace fragment', function () {
+      document.body.appendChild(el)
+      var vm = new Vue({
+        replace: true,
+        data: { test: 'foo' },
+        template: '<div>{{test}}</div><div>{{test + "bar"}}</div>'
+      })
+      vm.$mount(el)
+      expect(vm.$el).not.toBe(el)
+      expect(vm.$el.nextSibling.textContent).toBe('foo')
+      expect(vm.$el.nextSibling.nextSibling.textContent).toBe('foobar')
+      expect(document.body.contains(el)).toBe(false)
+      expect(document.body.lastChild).toBe(vm._fragmentEnd)
+      vm.$remove()
+    })
+
+    it('hooks', function () {
+      var hooks = ['created', 'beforeCompile', 'compiled', 'attached', 'ready']
+      var options = {
+        data: {
+          test: 'foo'
+        }
+      }
+      hooks.forEach(function (hook) {
+        options[hook] = jasmine.createSpy(hook)
+      })
+      var vm = new Vue(options)
+      expect(options.created).toHaveBeenCalled()
+      expect(options.beforeCompile).not.toHaveBeenCalled()
+      vm.$mount(el)
+      expect(options.beforeCompile).toHaveBeenCalled()
+      expect(options.compiled).toHaveBeenCalled()
+      expect(options.attached).not.toHaveBeenCalled()
+      expect(options.ready).not.toHaveBeenCalled()
+      vm.$appendTo(document.body)
+      expect(options.attached).toHaveBeenCalled()
+      expect(options.ready).toHaveBeenCalled()
+      vm.$remove()
+    })
+
+    it('warn against multiple calls', function () {
+      var vm = new Vue({
+        el: el
+      })
+      vm.$mount(el)
+      expect('$mount() should be called only once').toHaveBeenWarned()
+    })
+  })
+
+  describe('$destroy', function () {
+    it('normal', function () {
+      var vm = new Vue()
+      expect(vm._isDestroyed).toBe(false)
+      var data = vm._data
+      expect(data.__ob__.vms.length).toBe(1)
+      vm.$destroy()
+      expect(data.__ob__.vms.length).toBe(0)
+      expect(vm._isDestroyed).toBe(true)
+      expect(vm._watchers).toBeNull()
+      expect(vm.$el).toBeNull()
+      expect(vm.$parent).toBeNull()
+      expect(vm.$root).toBeNull()
+      expect(vm.$children).toBeNull()
+      expect(vm._directives).toBeNull()
+      expect(Object.keys(vm._events).length).toBe(0)
+    })
+
+    it('remove element', function () {
+      var el = document.createElement('div')
+      var parent = document.createElement('div')
+      parent.appendChild(el)
+      var vm = new Vue({ el: el })
+      vm.$destroy(true)
+      expect(parent.childNodes.length).toBe(0)
+      expect(el.__vue__).toBeNull()
+    })
+
+    it('hooks', function () {
+      var opts = {
+        beforeDestroy: jasmine.createSpy(),
+        destroyed: jasmine.createSpy(),
+        detached: jasmine.createSpy()
+      }
+      var el = opts.el = document.createElement('div')
+      document.body.appendChild(el)
+      var vm = new Vue(opts)
+      vm.$destroy(true)
+      expect(opts.beforeDestroy).toHaveBeenCalled()
+      expect(opts.destroyed).toHaveBeenCalled()
+      expect(opts.detached).toHaveBeenCalled()
+    })
+
+    // #1966
+    it('grandchild hooks', function () {
+      var grandChildBeforeDestroy = jasmine.createSpy()
+      var grandChildDestroyed = jasmine.createSpy()
+      var grandChildDetached = jasmine.createSpy()
+
+      var opts = {
+        template: '<div><test></test></div>',
+        components: {
+          test: {
+            template: '<div><test-inner></test-inner></div>',
+            components: {
+              'test-inner': {
+                beforeDestroy: grandChildBeforeDestroy,
+                destroyed: grandChildDestroyed,
+                detached: grandChildDetached
+              }
+            }
+          }
+        }
+      }
+      var el = opts.el = document.createElement('div')
+      document.body.appendChild(el)
+      var vm = new Vue(opts)
+      vm.$destroy(true)
+
+      expect(grandChildBeforeDestroy).toHaveBeenCalled()
+      expect(grandChildDestroyed).toHaveBeenCalled()
+      expect(grandChildDetached).toHaveBeenCalled()
+    })
+
+    it('parent', function () {
+      var parent = new Vue()
+      var child = new Vue({ parent: parent })
+      var child2 = new Vue({ parent: parent })
+      expect(parent.$children.length).toBe(2)
+      child.$destroy()
+      expect(parent.$children.length).toBe(1)
+      child2.$destroy()
+      expect(parent.$children.length).toBe(0)
+    })
+
+    it('children', function () {
+      var parent = new Vue()
+      var child = new Vue({ parent: parent })
+      parent.$destroy()
+      expect(child._isDestroyed).toBe(true)
+    })
+
+    it('directives', function () {
+      var spy = jasmine.createSpy('directive unbind')
+      var vm = new Vue({
+        el: document.createElement('div'),
+        template: '<div v-test></div>',
+        directives: {
+          test: {
+            unbind: spy
+          }
+        }
+      })
+      vm.$destroy()
+      expect(spy).toHaveBeenCalled()
+    })
+
+    it('watchers', function () {
+      var vm = new Vue({
+        el: document.createElement('div'),
+        template: '{{a}}',
+        data: { a: 1 }
+      })
+      vm.$watch('a', function () {})
+      var dirWatcher = vm._watchers[0]
+      var userWatcher = vm._watchers[1]
+      vm.$destroy()
+      expect(dirWatcher.active).toBe(false)
+      expect(userWatcher.active).toBe(false)
+    })
+
+    it('refuse multiple calls', function () {
+      var spy = jasmine.createSpy()
+      var vm = new Vue({
+        beforeDestroy: spy
+      })
+      vm.$destroy()
+      vm.$destroy()
+      expect(spy.calls.count()).toBe(1)
+    })
+
+    it('safely teardown partial compilation', function () {
+      var vm = new Vue({
+        template: '<test><partial name="hello"></partial></test>',
+        partials: {
+          hello: 'Hello {{name}}'
+        },
+        components: {
+          test: {
+            template: '<slot></slot>'
+          }
+        }
+      }).$mount()
+      expect(function () {
+        vm.$destroy()
+      }).not.toThrow()
+    })
+  })
+
+  describe('$compile', function () {
+    it('should partial compile and teardown stuff', function (done) {
+      var el = document.createElement('div')
+      var vm = new Vue({
+        el: el,
+        template: '{{a}}',
+        data: {
+          a: 'foo'
+        }
+      })
+      expect(vm._directives.length).toBe(1)
+      var partial = document.createElement('span')
+      partial.textContent = '{{a}}'
+      var decompile = vm.$compile(partial)
+      expect(partial.textContent).toBe('foo')
+      expect(vm._directives.length).toBe(2)
+      decompile()
+      expect(vm._directives.length).toBe(1)
+      vm.a = 'bar'
+      Vue.nextTick(function () {
+        expect(el.textContent).toBe('bar')
+        expect(partial.textContent).toBe('foo')
+        done()
+      })
+    })
+  })
+})
diff --git a/test/unit/specs/async_component_spec.js b/test/unit/specs/async_component_spec.js
new file mode 100644
index 00000000000..da0378e4a42
--- /dev/null
+++ b/test/unit/specs/async_component_spec.js
@@ -0,0 +1,228 @@
+var Vue = require('src')
+var _ = Vue.util
+
+describe('Async components', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+    document.body.appendChild(el)
+  })
+
+  afterEach(function () {
+    document.body.removeChild(el)
+  })
+
+  it('normal', function (done) {
+    var go = jasmine.createSpy()
+    new Vue({
+      el: el,
+      template: '<test foo="bar" @ready="go"></test>',
+      methods: {
+        go: go
+      },
+      components: {
+        test: function (resolve) {
+          setTimeout(function () {
+            resolve({
+              props: ['foo'],
+              template: '{{ foo }}',
+              ready: function () {
+                this.$emit('ready')
+              }
+            })
+            next()
+          }, 0)
+        }
+      }
+    })
+    function next () {
+      expect(el.textContent).toBe('bar')
+      expect(go).toHaveBeenCalled()
+      done()
+    }
+  })
+
+  it('dynamic', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<component :is="view"></component>',
+      data: {
+        view: 'view-a'
+      },
+      components: {
+        'view-a': function (resolve) {
+          setTimeout(function () {
+            resolve({
+              template: 'A'
+            })
+            step1()
+          }, 0)
+        },
+        'view-b': function (resolve) {
+          setTimeout(function () {
+            resolve({
+              template: 'B'
+            })
+            step2()
+          }, 0)
+        }
+      }
+    })
+    var aCalled = false
+    function step1 () {
+      // ensure A is resolved only once
+      expect(aCalled).toBe(false)
+      aCalled = true
+      expect(el.textContent).toBe('A')
+      vm.view = 'view-b'
+    }
+    function step2 () {
+      expect(el.textContent).toBe('B')
+      vm.view = 'view-a'
+      _.nextTick(function () {
+        expect(el.textContent).toBe('A')
+        done()
+      })
+    }
+  })
+
+  it('invalidate pending on dynamic switch', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<component :is="view"></component>',
+      data: {
+        view: 'view-a'
+      },
+      components: {
+        'view-a': function (resolve) {
+          setTimeout(function () {
+            resolve({
+              template: 'A'
+            })
+            step1()
+          }, 100)
+        },
+        'view-b': function (resolve) {
+          setTimeout(function () {
+            resolve({
+              template: 'B'
+            })
+            step2()
+          }, 200)
+        }
+      }
+    })
+    expect(el.textContent).toBe('')
+    vm.view = 'view-b'
+    function step1 () {
+      // called after A resolves, but A should have been
+      // invalidated so no Ctor should be set
+      expect(vm._directives[0].Component).toBe(null)
+    }
+    function step2 () {
+      // B should resolve successfully
+      expect(el.textContent).toBe('B')
+      done()
+    }
+  })
+
+  it('invalidate pending on teardown', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<test></test>',
+      data: {
+        view: 'view-a'
+      },
+      components: {
+        test: function (resolve) {
+          setTimeout(function () {
+            resolve({
+              template: 'A'
+            })
+            next()
+          }, 100)
+        }
+      }
+    })
+    expect(el.textContent).toBe('')
+    // cache directive isntance before destroy
+    var dir = vm._directives[0]
+    vm.$destroy()
+    function next () {
+      // called after A resolves, but A should have been
+      // invalidated so no Ctor should be set
+      expect(dir.Component).toBe(null)
+      done()
+    }
+  })
+
+  it('avoid duplicate requests', function (done) {
+    var factoryCallCount = 0
+    var instanceCount = 0
+    new Vue({
+      el: el,
+      template:
+        '<test></test>' +
+        '<test></test>',
+      components: {
+        test: factory
+      }
+    })
+    function factory (resolve) {
+      factoryCallCount++
+      setTimeout(function () {
+        resolve({
+          template: 'A',
+          created: function () {
+            instanceCount++
+          }
+        })
+        next()
+      }, 0)
+    }
+    function next () {
+      expect(factoryCallCount).toBe(1)
+      expect(el.textContent).toBe('AA')
+      expect(instanceCount).toBe(2)
+      done()
+    }
+  })
+
+  it('warn reject', function () {
+    new Vue({
+      el: el,
+      template: '<test></test>',
+      components: {
+        test: function (resolve, reject) {
+          reject('nooooo')
+        }
+      }
+    })
+    expect('Reason: nooooo').toHaveBeenWarned()
+  })
+
+  it('v-for', function (done) {
+    new Vue({
+      el: el,
+      template: '<test v-for="n in list" :n="n"></test>',
+      data: {
+        list: [1, 2, 3]
+      },
+      components: {
+        test: function (resolve) {
+          setTimeout(function () {
+            resolve({
+              props: ['n'],
+              template: '{{n}}'
+            })
+            next()
+          }, 0)
+        }
+      }
+    })
+    function next () {
+      expect(el.textContent).toBe('123')
+      done()
+    }
+  })
+})
diff --git a/test/unit/specs/batcher_spec.js b/test/unit/specs/batcher_spec.js
new file mode 100644
index 00000000000..973d579ffec
--- /dev/null
+++ b/test/unit/specs/batcher_spec.js
@@ -0,0 +1,121 @@
+var config = require('src/config')
+var batcher = require('src/batcher')
+var nextTick = require('src/util').nextTick
+
+describe('Batcher', function () {
+  var spy
+  beforeEach(function () {
+    spy = jasmine.createSpy('batcher')
+  })
+
+  it('pushWatcher', function (done) {
+    batcher.pushWatcher({
+      run: spy
+    })
+    nextTick(function () {
+      expect(spy.calls.count()).toBe(1)
+      done()
+    })
+  })
+
+  it('dedup', function (done) {
+    batcher.pushWatcher({
+      id: 1,
+      run: spy
+    })
+    batcher.pushWatcher({
+      id: 1,
+      run: spy
+    })
+    nextTick(function () {
+      expect(spy.calls.count()).toBe(1)
+      done()
+    })
+  })
+
+  it('allow diplicate when flushing', function (done) {
+    var job = {
+      id: 1,
+      run: spy
+    }
+    batcher.pushWatcher(job)
+    batcher.pushWatcher({
+      id: 2,
+      run: function () {
+        batcher.pushWatcher(job)
+      }
+    })
+    nextTick(function () {
+      expect(spy.calls.count()).toBe(2)
+      done()
+    })
+  })
+
+  it('calls user watchers after directive updates', function (done) {
+    var vals = []
+    function run () {
+      vals.push(this.id)
+    }
+    batcher.pushWatcher({
+      id: 2,
+      user: true,
+      run: function () {
+        run.call(this)
+        // user watcher triggering another directive update!
+        batcher.pushWatcher({
+          id: 3,
+          run: run
+        })
+      }
+    })
+    batcher.pushWatcher({
+      id: 1,
+      run: run
+    })
+    nextTick(function () {
+      expect(vals[0]).toBe(1)
+      expect(vals[1]).toBe(2)
+      expect(vals[2]).toBe(3)
+      done()
+    })
+  })
+
+  it('warn against infinite update loops', function (done) {
+    var count = 0
+    var job = {
+      id: 1,
+      run: function () {
+        count++
+        batcher.pushWatcher(job)
+      }
+    }
+    batcher.pushWatcher(job)
+    nextTick(function () {
+      expect(count).toBe(config._maxUpdateCount + 1)
+      expect('infinite update loop').toHaveBeenWarned()
+      done()
+    })
+  })
+
+  it('should call newly pushed watcher after current watcher is done', function (done) {
+    var callOrder = []
+    batcher.pushWatcher({
+      id: 1,
+      user: true,
+      run: function () {
+        callOrder.push(1)
+        batcher.pushWatcher({
+          id: 2,
+          run: function () {
+            callOrder.push(3)
+          }
+        })
+        callOrder.push(2)
+      }
+    })
+    nextTick(function () {
+      expect(callOrder.join()).toBe('1,2,3')
+      done()
+    })
+  })
+})
diff --git a/test/unit/specs/cache_spec.js b/test/unit/specs/cache_spec.js
new file mode 100644
index 00000000000..dac178f5749
--- /dev/null
+++ b/test/unit/specs/cache_spec.js
@@ -0,0 +1,76 @@
+var Cache = require('src/cache')
+
+/**
+ * Debug function to assert cache state
+ *
+ * @param {Cache} cache
+ */
+
+function toString (cache) {
+  var s = ''
+  var entry = cache.head
+  while (entry) {
+    s += String(entry.key) + ':' + entry.value
+    entry = entry.newer
+    if (entry) {
+      s += ' < '
+    }
+  }
+  return s
+}
+
+describe('Cache', function () {
+  var c = new Cache(4)
+
+  it('put', function () {
+    c.put('adam', 29)
+    c.put('john', 26)
+    c.put('angela', 24)
+    c.put('bob', 48)
+    expect(c.size).toBe(4)
+    expect(toString(c)).toBe('adam:29 < john:26 < angela:24 < bob:48')
+  })
+
+  it('put with same key', function () {
+    var same = new Cache(4)
+    same.put('john', 29)
+    same.put('john', 26)
+    same.put('john', 24)
+    same.put('john', 48)
+    expect(same.size).toBe(1)
+    expect(toString(same)).toBe('john:48')
+  })
+
+  it('get', function () {
+    expect(c.get('adam')).toBe(29)
+    expect(c.get('john')).toBe(26)
+    expect(c.get('angela')).toBe(24)
+    expect(c.get('bob')).toBe(48)
+    expect(toString(c)).toBe('adam:29 < john:26 < angela:24 < bob:48')
+
+    expect(c.get('angela')).toBe(24)
+    // angela should now be the tail
+    expect(toString(c)).toBe('adam:29 < john:26 < bob:48 < angela:24')
+  })
+
+  it('expire', function () {
+    c.put('ygwie', 81)
+    expect(c.size).toBe(4)
+    expect(toString(c)).toBe('john:26 < bob:48 < angela:24 < ygwie:81')
+    expect(c.get('adam')).toBeUndefined()
+  })
+
+  it('shift', function () {
+    var shift = new Cache(4)
+    shift.put('adam', 29)
+    shift.put('john', 26)
+    shift.put('angela', 24)
+    shift.put('bob', 48)
+
+    shift.shift()
+    shift.shift()
+    shift.shift()
+    expect(shift.size).toBe(1)
+    expect(toString(shift)).toBe('bob:48')
+  })
+})
diff --git a/test/unit/specs/compiler/compile_spec.js b/test/unit/specs/compiler/compile_spec.js
new file mode 100644
index 00000000000..f1cab5f9e57
--- /dev/null
+++ b/test/unit/specs/compiler/compile_spec.js
@@ -0,0 +1,718 @@
+var Vue = require('src')
+var _ = require('src/util')
+var FragmentFactory = require('src/fragment/factory')
+var compiler = require('src/compiler')
+var compile = compiler.compile
+var publicDirectives = require('src/directives/public')
+var internalDirectives = require('src/directives/internal')
+
+describe('Compile', function () {
+  var vm, el, data, directiveBind, directiveTeardown
+  beforeEach(function () {
+    // We mock vms here so we can assert what the generated
+    // linker functions do.
+    el = document.createElement('div')
+    data = {}
+    directiveBind = jasmine.createSpy('bind')
+    directiveTeardown = jasmine.createSpy('teardown')
+    vm = {
+      $options: {},
+      _data: {},
+      _directives: [],
+      _bindDir: function (descriptor, node) {
+        this._directives.push({
+          name: descriptor.name,
+          descriptor: descriptor,
+          _bind: function () {
+            directiveBind(this.name)
+          },
+          _teardown: directiveTeardown
+        })
+      },
+      $get: function (exp) {
+        return (new Vue()).$get(exp)
+      },
+      $eval: function (value) {
+        return data[value]
+      },
+      $interpolate: function (value) {
+        return data[value]
+      },
+      _context: {
+        _directives: [],
+        $get: function (v) {
+          return 'from parent: ' + v
+        }
+      }
+    }
+    spyOn(vm, '_bindDir').and.callThrough()
+    spyOn(vm, '$eval').and.callThrough()
+    spyOn(vm, '$interpolate').and.callThrough()
+  })
+
+  it('normal directives', function () {
+    el.setAttribute('v-a', 'b')
+    el.innerHTML = '<p v-a:hello.a.b="a" v-b="1">hello</p><div v-b.literal="foo"></div>'
+    var defA = { priority: 250 }
+    var defB = { priority: 1100 }
+    var options = _.mergeOptions(Vue.options, {
+      directives: {
+        a: defA,
+        b: defB
+      }
+    })
+    var linker = compile(el, options)
+    expect(typeof linker).toBe('function')
+    linker(vm, el)
+    expect(directiveBind.calls.count()).toBe(4)
+    expect(vm._bindDir.calls.count()).toBe(4)
+
+    // check if we are in firefox, which has different
+    // attribute interation order
+    var isAttrReversed = el.firstChild.attributes[0].name === 'v-b'
+
+    // 1
+    var args = vm._bindDir.calls.argsFor(0)
+    expect(args[0].name).toBe('a')
+    expect(args[0].expression).toBe('b')
+    expect(args[0].def).toBe(defA)
+    expect(args[1]).toBe(el)
+    // 2
+    args = vm._bindDir.calls.argsFor(isAttrReversed ? 2 : 1)
+    expect(args[0].name).toBe('a')
+    expect(args[0].expression).toBe('a')
+    expect(args[0].def).toBe(defA)
+    // args + multiple modifiers
+    expect(args[0].arg).toBe('hello')
+    expect(args[0].modifiers.a).toBe(true)
+    expect(args[0].modifiers.b).toBe(true)
+    expect(args[1]).toBe(el.firstChild)
+    // 3 (expression literal)
+    args = vm._bindDir.calls.argsFor(isAttrReversed ? 1 : 2)
+    expect(args[0].name).toBe('b')
+    expect(args[0].expression).toBe('1')
+    expect(args[0].def).toBe(defB)
+    expect(args[1]).toBe(el.firstChild)
+    // 4 (explicit literal)
+    args = vm._bindDir.calls.argsFor(3)
+    expect(args[0].name).toBe('b')
+    expect(args[0].expression).toBe('foo')
+    expect(args[0].def).toBe(defB)
+    expect(args[0].modifiers.literal).toBe(true)
+    expect(args[1]).toBe(el.lastChild)
+    // check the priority sorting
+    // the "b"s should be called first!
+    expect(directiveBind.calls.argsFor(0)[0]).toBe('b')
+    expect(directiveBind.calls.argsFor(1)[0]).toBe('b')
+    expect(directiveBind.calls.argsFor(2)[0]).toBe('a')
+    expect(directiveBind.calls.argsFor(3)[0]).toBe('a')
+  })
+
+  it('v-bind shorthand', function () {
+    el.setAttribute(':class', 'a')
+    el.setAttribute(':style', 'b')
+    el.setAttribute(':title', 'c')
+
+    // The order of setAttribute is not guaranteed to be the same with
+    // the order of attribute enumberation, therefore we need to save
+    // it here!
+    var descriptors = {
+      ':class': {
+        name: 'class',
+        attr: ':class',
+        expression: 'a',
+        def: internalDirectives.class
+      },
+      ':style': {
+        name: 'style',
+        attr: ':style',
+        expression: 'b',
+        def: internalDirectives.style
+      },
+      ':title': {
+        name: 'bind',
+        attr: ':title',
+        expression: 'c',
+        arg: 'title',
+        def: publicDirectives.bind
+      }
+    }
+    var expects = [].map.call(el.attributes, function (attr) {
+      return descriptors[attr.name]
+    })
+
+    var linker = compile(el, Vue.options)
+    linker(vm, el)
+    expect(vm._bindDir.calls.count()).toBe(3)
+
+    expects.forEach(function (e, i) {
+      var args = vm._bindDir.calls.argsFor(i)
+      for (var key in e) {
+        expect(args[0][key]).toBe(e[key])
+      }
+      expect(args[1]).toBe(el)
+    })
+  })
+
+  it('v-on shorthand', function () {
+    el.innerHTML = '<div @click="a++"></div>'
+    el = el.firstChild
+    var linker = compile(el, Vue.options)
+    linker(vm, el)
+    expect(vm._bindDir.calls.count()).toBe(1)
+    var args = vm._bindDir.calls.argsFor(0)
+    expect(args[0].name).toBe('on')
+    expect(args[0].expression).toBe('a++')
+    expect(args[0].arg).toBe('click')
+    expect(args[0].def).toBe(publicDirectives.on)
+    expect(args[1]).toBe(el)
+  })
+
+  it('text interpolation', function () {
+    data.b = 'yeah'
+    el.innerHTML = '{{a}} and {{*b}}'
+    var def = Vue.options.directives.text
+    var linker = compile(el, Vue.options)
+    linker(vm, el)
+    // expect 1 call because one-time bindings do not generate a directive.
+    expect(vm._bindDir.calls.count()).toBe(1)
+    var args = vm._bindDir.calls.argsFor(0)
+    expect(args[0].name).toBe('text')
+    expect(args[0].expression).toBe('a')
+    expect(args[0].def).toBe(def)
+    // skip the node because it's generated in the linker fn via cloneNode
+    // expect $eval to be called during onetime
+    expect(vm.$eval).toHaveBeenCalledWith('b')
+    // {{a}} is mocked so it's a space.
+    // but we want to make sure {{*b}} worked.
+    expect(el.innerHTML).toBe('  and yeah')
+  })
+
+  it('text interpolation, adjacent nodes', function () {
+    data.b = 'yeah'
+    el.appendChild(document.createTextNode('{{a'))
+    el.appendChild(document.createTextNode('}} and {{'))
+    el.appendChild(document.createTextNode('*b}}'))
+    var def = Vue.options.directives.text
+    var linker = compile(el, Vue.options)
+    linker(vm, el)
+    // expect 1 call because one-time bindings do not generate a directive.
+    expect(vm._bindDir.calls.count()).toBe(1)
+    var args = vm._bindDir.calls.argsFor(0)
+    expect(args[0].name).toBe('text')
+    expect(args[0].expression).toBe('a')
+    expect(args[0].def).toBe(def)
+    // skip the node because it's generated in the linker fn via cloneNode
+    // expect $eval to be called during onetime
+    expect(vm.$eval).toHaveBeenCalledWith('b')
+    // {{a}} is mocked so it's a space.
+    // but we want to make sure {{*b}} worked.
+    expect(el.innerHTML).toBe('  and yeah')
+  })
+
+  it('adjacent text nodes with no interpolation', function () {
+    el.appendChild(document.createTextNode('a'))
+    el.appendChild(document.createTextNode('b'))
+    el.appendChild(document.createTextNode('c'))
+    var linker = compile(el, Vue.options)
+    linker(vm, el)
+    expect(el.innerHTML).toBe('abc')
+  })
+
+  it('inline html', function () {
+    data.html = '<div>foo</div>'
+    el.innerHTML = '{{{html}}} {{{*html}}}'
+    var htmlDef = Vue.options.directives.html
+    var linker = compile(el, Vue.options)
+    linker(vm, el)
+    expect(vm._bindDir.calls.count()).toBe(1)
+    var htmlArgs = vm._bindDir.calls.argsFor(0)
+    expect(htmlArgs[0].name).toBe('html')
+    expect(htmlArgs[0].expression).toBe('html')
+    expect(htmlArgs[0].def).toBe(htmlDef)
+    // with placeholder comments & interpolated one-time html
+    expect(el.innerHTML).toBe('<!--v-html--> <div>foo</div>')
+  })
+
+  it('terminal directives', function () {
+    el.innerHTML =
+      '<div v-for="item in items"><p v-a="b"></p></div>' + // v-for
+      '<div v-pre><p v-a="b"></p></div>' // v-pre
+    var def = Vue.options.directives.for
+    var linker = compile(el, Vue.options)
+    linker(vm, el)
+    // expect 1 call because terminal should return early and let
+    // the directive handle the rest.
+    expect(vm._bindDir.calls.count()).toBe(1)
+    var args = vm._bindDir.calls.argsFor(0)
+    expect(args[0].name).toBe('for')
+    expect(args[0].expression).toBe('item in items')
+    expect(args[0].def).toBe(def)
+    expect(args[1]).toBe(el.firstChild)
+  })
+
+  it('custom terminal directives', function () {
+    var defTerminal = {
+      terminal: true,
+      priority: Vue.options.directives.if.priority + 1
+    }
+    var options = _.mergeOptions(Vue.options, {
+      directives: { term: defTerminal }
+    })
+    el.innerHTML = '<div v-term:arg1.modifier1.modifier2="foo"></div>'
+    var linker = compile(el, options)
+    linker(vm, el)
+    expect(vm._bindDir.calls.count()).toBe(1)
+    var args = vm._bindDir.calls.argsFor(0)
+    expect(args[0].name).toBe('term')
+    expect(args[0].expression).toBe('foo')
+    expect(args[0].attr).toBe('v-term:arg1.modifier1.modifier2')
+    expect(args[0].arg).toBe('arg1')
+    expect(args[0].modifiers.modifier1).toBe(true)
+    expect(args[0].modifiers.modifier2).toBe(true)
+    expect(args[0].def).toBe(defTerminal)
+  })
+
+  it('custom terminal directives priority', function () {
+    var defTerminal = {
+      terminal: true,
+      priority: Vue.options.directives.if.priority + 1
+    }
+    var options = _.mergeOptions(Vue.options, {
+      directives: { term: defTerminal }
+    })
+    el.innerHTML = '<div v-term:arg1 v-if="ok"></div>'
+    var linker = compile(el, options)
+    linker(vm, el)
+    expect(vm._bindDir.calls.count()).toBe(1)
+    var args = vm._bindDir.calls.argsFor(0)
+    expect(args[0].name).toBe('term')
+    expect(args[0].expression).toBe('')
+    expect(args[0].attr).toBe('v-term:arg1')
+    expect(args[0].arg).toBe('arg1')
+    expect(args[0].def).toBe(defTerminal)
+  })
+
+  it('custom element components', function () {
+    var options = _.mergeOptions(Vue.options, {
+      components: {
+        'my-component': {}
+      }
+    })
+    el.innerHTML = '<my-component><div v-a="b"></div></my-component>'
+    var linker = compile(el, options)
+    linker(vm, el)
+    expect(vm._bindDir.calls.count()).toBe(1)
+    var args = vm._bindDir.calls.argsFor(0)
+    expect(args[0].name).toBe('component')
+    expect(args[0].expression).toBe('my-component')
+    expect(args[0].modifiers.literal).toBe(true)
+    expect(args[0].def).toBe(internalDirectives.component)
+    expect(getWarnCount()).toBe(0)
+  })
+
+  it('props', function () {
+    var bindingModes = Vue.config._propBindingModes
+    var props = {
+      testNormal: null,
+      testLiteral: null,
+      testBoolean: { type: Boolean },
+      testTwoWay: null,
+      twoWayWarn: null,
+      testOneTime: null,
+      optimizeLiteral: null,
+      optimizeLiteralStr: null,
+      optimizeLiteralNegativeNumber: null,
+      literalWithFilter: null
+    }
+    el.innerHTML = '<div ' +
+      'v-bind:test-normal="a" ' +
+      'test-literal="1" ' +
+      'test-boolean ' +
+      ':optimize-literal="1" ' +
+      ':optimize-literal-str="\'true\'"' +
+      ':optimize-literal-negative-number="-1"' +
+      ':test-two-way.sync="a" ' +
+      ':two-way-warn.sync="a + 1" ' +
+      ':test-one-time.once="a" ' +
+      ':literal-with-filter="\'FOO\' | lowercase"' +
+      '></div>'
+    compiler.compileAndLinkProps(vm, el.firstChild, props)
+    // check bindDir calls:
+    // skip literal and one time, but not literal with filter
+    expect(vm._bindDir.calls.count()).toBe(4)
+    // literal
+    expect(vm.testLiteral).toBe('1')
+    expect(vm.testBoolean).toBe(true)
+    expect(vm.optimizeLiteral).toBe(1)
+    expect(vm.optimizeLiteralStr).toBe('true')
+    expect(vm.optimizeLiteralNegativeNumber).toBe(-1)
+    // one time
+    expect(vm.testOneTime).toBe('from parent: a')
+    // normal
+    var args = vm._bindDir.calls.argsFor(0)
+    var prop = args[0].prop
+    expect(args[0].name).toBe('prop')
+    expect(prop.path).toBe('testNormal')
+    expect(prop.parentPath).toBe('a')
+    expect(prop.mode).toBe(bindingModes.ONE_WAY)
+    // two way
+    args = vm._bindDir.calls.argsFor(1)
+    prop = args[0].prop
+    expect(args[0].name).toBe('prop')
+    expect(prop.path).toBe('testTwoWay')
+    expect(prop.parentPath).toBe('a')
+    expect(prop.mode).toBe(bindingModes.TWO_WAY)
+    // two way warn
+    expect('non-settable parent path').toHaveBeenWarned()
+    // literal with filter
+    args = vm._bindDir.calls.argsFor(3)
+    prop = args[0].prop
+    expect(args[0].name).toBe('prop')
+    expect(prop.path).toBe('literalWithFilter')
+    expect(prop.parentPath).toBe("'FOO'")
+    expect(prop.filters.length).toBe(1)
+    expect(prop.mode).toBe(bindingModes.ONE_WAY)
+  })
+
+  it('props on root instance', function () {
+    // temporarily remove vm.$parent
+    var context = vm._context
+    vm._context = null
+    el.setAttribute('v-bind:a', '"foo"')
+    el.setAttribute(':b', '[1,2,3]')
+    compiler.compileAndLinkProps(vm, el, { a: null, b: null })
+    expect(vm._bindDir.calls.count()).toBe(0)
+    expect(vm.a).toBe('foo')
+    expect(vm.b.join(',')).toBe('1,2,3')
+    // restore parent mock
+    vm._context = context
+  })
+
+  it('DocumentFragment', function () {
+    var frag = document.createDocumentFragment()
+    frag.appendChild(el)
+    var el2 = document.createElement('div')
+    frag.appendChild(el2)
+    el.innerHTML = '{{*a}}'
+    el2.innerHTML = '{{*b}}'
+    data.a = 'A'
+    data.b = 'B'
+    var linker = compile(frag, Vue.options)
+    linker(vm, frag)
+    expect(el.innerHTML).toBe('A')
+    expect(el2.innerHTML).toBe('B')
+  })
+
+  it('partial compilation', function () {
+    el.innerHTML = '<div v-bind:test="abc">{{bcd}}<p v-show="ok"></p></div>'
+    var linker = compile(el, Vue.options, true)
+    var decompile = linker(vm, el)
+    expect(vm._directives.length).toBe(3)
+    decompile()
+    expect(directiveTeardown.calls.count()).toBe(3)
+    expect(vm._directives.length).toBe(0)
+  })
+
+  it('skip script tags', function () {
+    el.innerHTML = '<script type="x/template">{{test}}</script>'
+    var linker = compile(el, Vue.options)
+    linker(vm, el)
+    expect(vm._bindDir.calls.count()).toBe(0)
+  })
+
+  it('should handle container/replacer directives with same name', function () {
+    var parentSpy = jasmine.createSpy()
+    var childSpy = jasmine.createSpy()
+    vm = new Vue({
+      el: el,
+      template:
+        '<test class="a" v-on:click="test(1)"></test>',
+      methods: {
+        test: parentSpy
+      },
+      components: {
+        test: {
+          template: '<div class="b" v-on:click="test(2)"></div>',
+          replace: true,
+          methods: {
+            test: childSpy
+          }
+        }
+      }
+    })
+    expect(vm.$el.firstChild.className).toBe('b a')
+    var e = document.createEvent('HTMLEvents')
+    e.initEvent('click', true, true)
+    vm.$el.firstChild.dispatchEvent(e)
+    expect(parentSpy).toHaveBeenCalledWith(1)
+    expect(childSpy).toHaveBeenCalledWith(2)
+  })
+
+  it('should teardown props and replacer directives when unlinking', function () {
+    var vm = new Vue({
+      el: el,
+      template: '<test :msg="msg"></test>',
+      data: {
+        msg: 'foo'
+      },
+      components: {
+        test: {
+          props: ['msg'],
+          template: '<div v-show="true"></div>',
+          replace: true
+        }
+      }
+    })
+    var dirs = vm.$children[0]._directives
+    expect(dirs.length).toBe(2)
+    vm.$children[0].$destroy()
+    var i = dirs.length
+    while (i--) {
+      expect(dirs[i]._bound).toBe(false)
+    }
+  })
+
+  it('should remove parent container directives from parent when unlinking', function () {
+    var vm = new Vue({
+      el: el,
+      template:
+        '<test v-show="ok"></test>',
+      data: {
+        ok: true
+      },
+      components: {
+        test: {
+          template: 'foo'
+        }
+      }
+    })
+    expect(el.firstChild.style.display).toBe('')
+    expect(vm._directives.length).toBe(2)
+    expect(vm.$children.length).toBe(1)
+    vm.$children[0].$destroy()
+    expect(vm._directives.length).toBe(1)
+    expect(vm.$children.length).toBe(0)
+  })
+
+  it('should remove transcluded directives from parent when unlinking (component)', function () {
+    var vm = new Vue({
+      el: el,
+      template:
+        '<test>{{test}}</test>',
+      data: {
+        test: 'parent'
+      },
+      components: {
+        test: {
+          template: '<slot></slot>'
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('parent')
+    expect(vm._directives.length).toBe(2)
+    expect(vm.$children.length).toBe(1)
+    vm.$children[0].$destroy()
+    expect(vm._directives.length).toBe(1)
+    expect(vm.$children.length).toBe(0)
+  })
+
+  it('should remove transcluded directives from parent when unlinking (v-if + component)', function (done) {
+    var vm = new Vue({
+      el: el,
+      template:
+        '<div v-if="ok">' +
+          '<test>{{test}}</test>' +
+        '</div>',
+      data: {
+        test: 'parent',
+        ok: true
+      },
+      components: {
+        test: {
+          template: '<slot></slot>'
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('parent')
+    expect(vm._directives.length).toBe(3)
+    expect(vm.$children.length).toBe(1)
+    vm.ok = false
+    _.nextTick(function () {
+      expect(vm.$el.textContent).toBe('')
+      expect(vm._directives.length).toBe(1)
+      expect(vm.$children.length).toBe(0)
+      done()
+    })
+  })
+
+  it('element directive', function () {
+    new Vue({
+      el: el,
+      template: '<test>{{a}}</test>',
+      elementDirectives: {
+        test: {
+          bind: function () {
+            this.el.setAttribute('test', '1')
+          }
+        }
+      }
+    })
+    // should be terminal
+    expect(el.innerHTML).toBe('<test test="1">{{a}}</test>')
+  })
+
+  it('attribute interpolation', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<div id="{{a}}" class="b bla-{{c}} d"></div>',
+      data: {
+        a: 'aaa',
+        c: 'ccc'
+      }
+    })
+    expect(el.firstChild.id).toBe('aaa')
+    expect(el.firstChild.className).toBe('b bla-ccc d')
+    vm.a = 'aa'
+    vm.c = 'cc'
+    _.nextTick(function () {
+      expect(el.firstChild.id).toBe('aa')
+      expect(el.firstChild.className).toBe('b bla-cc d')
+      done()
+    })
+  })
+
+  it('attribute interpolation: one-time', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<div id="{{a}} b {{*c}}"></div>',
+      data: {
+        a: 'aaa',
+        c: 'ccc'
+      }
+    })
+    expect(el.firstChild.id).toBe('aaa b ccc')
+    vm.a = 'aa'
+    vm.c = 'cc'
+    _.nextTick(function () {
+      expect(el.firstChild.id).toBe('aa b ccc')
+      done()
+    })
+  })
+
+  it('attribute interpolation: special cases', function () {
+    new Vue({
+      el: el,
+      template: '<label for="{{a}}" data-test="{{b}}"></label><form accept-charset="{{c}}"></form>',
+      data: {
+        a: 'aaa',
+        b: 'bbb',
+        c: 'UTF-8'
+      }
+    })
+    expect(el.innerHTML).toBe('<label for="aaa" data-test="bbb"></label><form accept-charset="UTF-8"></form>')
+  })
+
+  it('attribute interpolation: warn invalid', function () {
+    new Vue({
+      el: el,
+      template: '<div v-text="{{a}}"></div>',
+      data: {
+        a: '123'
+      }
+    })
+    expect(el.innerHTML).toBe('<div></div>')
+    expect('attribute interpolation is not allowed in Vue.js directives').toHaveBeenWarned()
+  })
+
+  it('attribute interpolation: warn mixed usage with v-bind', function () {
+    new Vue({
+      el: el,
+      template: '<div class="{{a}}" :class="bcd"></div>',
+      data: {
+        a: 'foo'
+      }
+    })
+    expect('Do not mix mustache interpolation and v-bind').toHaveBeenWarned()
+  })
+
+  it('warn directives on fragment instances', function () {
+    new Vue({
+      el: el,
+      template: '<test id="foo" class="ok" :prop="123"></test>',
+      components: {
+        test: {
+          replace: true,
+          props: ['prop'],
+          template: '{{prop}}'
+        }
+      }
+    })
+    expect(getWarnCount()).toBe(1)
+    expect([
+      'Attributes "id", "class" are ignored on component <test>',
+      'Attributes "class", "id" are ignored on component <test>'
+    ]).toHaveBeenWarned()
+  })
+
+  it('should compile component container directives using correct context', function () {
+    new Vue({
+      el: el,
+      directives: {
+        test: {
+          bind: function () {
+            this.el.textContent = 'worked!'
+          }
+        }
+      },
+      template: '<comp v-test></comp>',
+      components: { comp: { template: '<div></div>' }}
+    })
+    expect(el.textContent).toBe('worked!')
+    expect(getWarnCount()).toBe(0)
+  })
+
+  // #xxx
+  it('should compile build-in terminal directive wihtout loop', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: { show: false },
+      template: '<p v-if:arg1.modifier1="show">hello world</p>'
+    })
+    vm.show = true
+    _.nextTick(function () {
+      expect(el.textContent).toBe('hello world')
+      done()
+    })
+  })
+
+  it('should compile custom terminal directive wihtout loop', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: { show: false },
+      template: '<p v-if="show" v-inject:modal.modifier1="foo">hello world</p>',
+      directives: {
+        inject: {
+          terminal: true,
+          priority: Vue.options.directives.if.priority + 1,
+          bind: function () {
+            this.anchor = _.createAnchor('v-inject')
+            _.replace(this.el, this.anchor)
+            var factory = new FragmentFactory(this.vm, this.el)
+            this.frag = factory.create(this._host, this._scope, this._frag)
+            this.frag.before(this.anchor)
+          },
+          unbind: function () {
+            this.frag.remove()
+            _.replace(this.anchor, this.el)
+          }
+        }
+      }
+    })
+    vm.show = true
+    _.nextTick(function () {
+      expect(el.textContent).toBe('hello world')
+      done()
+    })
+  })
+})
diff --git a/test/unit/specs/compiler/transclude_spec.js b/test/unit/specs/compiler/transclude_spec.js
new file mode 100644
index 00000000000..b6c4c405705
--- /dev/null
+++ b/test/unit/specs/compiler/transclude_spec.js
@@ -0,0 +1,141 @@
+var transclude = require('src/compiler').transclude
+var Vue = require('src')
+var _ = require('src/util')
+
+describe('Transclude', function () {
+  var el, options
+  beforeEach(function () {
+    el = document.createElement('div')
+    options = _.extend({}, Vue.options)
+  })
+
+  it('normal', function () {
+    var res = transclude(el, options)
+    expect(res).toBe(el)
+  })
+
+  it('template', function () {
+    options.template = '{{hi}}'
+    var res = transclude(el, options)
+    expect(res).toBe(el)
+    expect(res.innerHTML).toBe('{{hi}}')
+  })
+
+  it('template invalid', function () {
+    options.template = '#non-existent-stuff'
+    var res = transclude(el, options)
+    expect(res).toBeUndefined()
+    expect('Invalid template option').toHaveBeenWarned()
+  })
+
+  it('template replace', function () {
+    el.className = 'hello'
+    options.template = '<div>{{hi}}</div>'
+    options.replace = true
+    var res = transclude(el, options)
+    expect(res).not.toBe(el)
+    expect(res.tagName).toBe('DIV')
+    expect(res.className).toBe('hello')
+    expect(res.innerHTML).toBe('{{hi}}')
+  })
+
+  it('template replace -> fragment instance', function () {
+    var res
+    options.replace = true
+
+    // multiple root
+    options.template = '<div></div><div></div>'
+    res = transclude(el, options)
+    expect(res.nodeType).toBe(11)
+
+    // non-element
+    options.template = '{{hi}}'
+    res = transclude(el, options)
+    expect(res.nodeType).toBe(11)
+
+    // single component: <component>
+    options.template = '<component bind-is="hi"></component>'
+    res = transclude(el, options)
+    expect(res.nodeType).toBe(11)
+
+    // single component: custom element
+    options.template = '<test></test>'
+    options.components = { test: {}}
+    res = transclude(el, options)
+    expect(res.nodeType).toBe(11)
+
+    // single component: is
+    options.template = '<div is="test"></div>'
+    res = transclude(el, options)
+    expect(res.nodeType).toBe(11)
+
+    // element directive
+    options.template = '<el-dir></el-dir>'
+    options.elementDirectives = { 'el-dir': {}}
+    res = transclude(el, options)
+    expect(res.nodeType).toBe(11)
+
+    // v-for
+    options.template = '<div v-for="item in list"></div>'
+    res = transclude(el, options)
+    expect(res.nodeType).toBe(11)
+
+    // v-if
+    options.template = '<div v-if="ok"></div>'
+    res = transclude(el, options)
+    expect(res.nodeType).toBe(11)
+  })
+
+  it('direct fragment instance', function () {
+    var frag = document.createDocumentFragment()
+    frag.appendChild(el)
+    var res = transclude(frag, options)
+    expect(res).toBe(frag)
+    expect(res.childNodes.length).toBe(3)
+    expect(res.childNodes[0].nodeType).toBe(3)
+    expect(res.childNodes[1]).toBe(el)
+    expect(res.childNodes[2].nodeType).toBe(3)
+  })
+
+  it('template element', function () {
+    var tpl = document.createElement('template')
+    tpl.innerHTML = '<div>123</div>'
+    var res = transclude(tpl, options)
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(3)
+    expect(res.childNodes[0].nodeType).toBe(3)
+    expect(res.childNodes[1].textContent).toBe('123')
+    expect(res.childNodes[2].nodeType).toBe(3)
+  })
+
+  it('replacer attr should overwrite container attr of same name, except class should be merged', function () {
+    el.setAttribute('class', 'test other')
+    el.setAttribute('title', 'parent')
+    options.template = '<div class="other ok" title="child"></div>'
+    options.replace = true
+    options._asComponent = true
+    var res = transclude(el, options)
+    expect(res.getAttribute('class')).toBe('other ok test')
+    expect(res.getAttribute('title')).toBe('child')
+  })
+
+  // #2789
+  it('empty class merge', function () {
+    el.setAttribute('class', '')
+    options.template = '<div class="test"></div>'
+    options.replace = true
+    options._asComponent = true
+    var res = transclude(el, options)
+    expect(res.getAttribute('class')).toBe('test')
+  })
+
+  it('class merge for svg elements', function () {
+    el.setAttribute('class', 'test')
+    options.template = '<circle class="other"></circle>'
+    options.replace = true
+    options._asComponent = true
+    var res = transclude(el, options)
+    expect(res.namespaceURI).toBe('http://www.w3.org/2000/svg')
+    expect(res.getAttribute('class')).toBe('other test')
+  })
+})
diff --git a/test/unit/specs/directive_spec.js b/test/unit/specs/directive_spec.js
new file mode 100644
index 00000000000..94d9dcdb914
--- /dev/null
+++ b/test/unit/specs/directive_spec.js
@@ -0,0 +1,191 @@
+var Vue = require('src')
+var Directive = require('src/directive')
+var nextTick = Vue.nextTick
+
+describe('Directive', function () {
+  var el, vm, def
+  beforeEach(function () {
+    el = document.createElement('div')
+    def = {
+      params: ['foo', 'keBab'],
+      paramWatchers: {
+        foo: jasmine.createSpy('foo')
+      },
+      bind: jasmine.createSpy('bind'),
+      update: jasmine.createSpy('update'),
+      unbind: jasmine.createSpy('unbind')
+    }
+    vm = new Vue({
+      data: {
+        a: 1,
+        b: { c: { d: 2 }}
+      },
+      filters: {
+        test: function (v) {
+          return v * 2
+        }
+      },
+      directives: {
+        test: def
+      }
+    })
+  })
+
+  it('normal', function (done) {
+    var d = new Directive({
+      name: 'test',
+      def: def,
+      expression: 'a',
+      modifiers: {
+        literal: false
+      },
+      filters: [{ name: 'test' }]
+    }, vm, el)
+    d._bind()
+    // properties
+    expect(d.el).toBe(el)
+    expect(d.name).toBe('test')
+    expect(d.vm).toBe(vm)
+    expect(d.expression).toBe('a')
+    expect(d.literal).toBe(false)
+    // init calls
+    expect(def.bind).toHaveBeenCalled()
+    expect(def.update).toHaveBeenCalledWith(2)
+    expect(d._bound).toBe(true)
+    vm.a = 2
+    nextTick(function () {
+      expect(def.update).toHaveBeenCalledWith(4, 2)
+      // teardown
+      d._teardown()
+      expect(def.unbind).toHaveBeenCalled()
+      expect(d._bound).toBe(false)
+      expect(d._watcher).toBe(null)
+      done()
+    })
+  })
+
+  it('literal', function () {
+    var d = new Directive({
+      name: 'test',
+      expression: 'a',
+      raw: 'a',
+      def: def,
+      modifiers: {
+        literal: true
+      }
+    }, vm, el)
+    d._bind()
+    expect(d._watcher).toBeUndefined()
+    expect(d.expression).toBe('a')
+    expect(d.bind).toHaveBeenCalled()
+    expect(d.update).toHaveBeenCalledWith('a')
+  })
+
+  it('inline statement', function () {
+    def.acceptStatement = true
+    var spy = jasmine.createSpy()
+    vm.$options.filters.test = function (fn) {
+      spy()
+      return function () {
+        // call it twice
+        fn()
+        fn()
+      }
+    }
+    var d = new Directive({
+      name: 'test',
+      expression: 'a++',
+      filters: [{name: 'test'}],
+      def: def
+    }, vm, el)
+    d._bind()
+    expect(d._watcher).toBeUndefined()
+    expect(d.bind).toHaveBeenCalled()
+    var wrappedFn = d.update.calls.argsFor(0)[0]
+    expect(typeof wrappedFn).toBe('function')
+    // test invoke the wrapped fn
+    wrappedFn()
+    expect(vm.a).toBe(3)
+  })
+
+  it('two-way', function (done) {
+    def.twoWay = true
+    vm.$options.filters.test = {
+      write: function (v) {
+        return v * 3
+      }
+    }
+    var d = new Directive({
+      name: 'test',
+      expression: 'a',
+      filters: [{name: 'test'}],
+      def: def
+    }, vm, el)
+    d._bind()
+    d.set(2)
+    expect(vm.a).toBe(6)
+    nextTick(function () {
+      // should have no update calls
+      expect(def.update.calls.count()).toBe(1)
+      done()
+    })
+  })
+
+  it('deep', function (done) {
+    def.deep = true
+    var d = new Directive({
+      name: 'test',
+      expression: 'b',
+      def: def
+    }, vm, el)
+    d._bind()
+    vm.b.c.d = 3
+    nextTick(function () {
+      expect(def.update.calls.count()).toBe(2)
+      done()
+    })
+  })
+
+  it('function def', function () {
+    var d = new Directive({
+      name: 'test',
+      expression: 'a',
+      def: def.update
+    }, vm, el)
+    d._bind()
+    expect(d.update).toBe(def.update)
+    expect(def.update).toHaveBeenCalled()
+  })
+
+  it('static params', function () {
+    el.setAttribute('foo', 'hello')
+    el.setAttribute('ke-bab', 'yo')
+    var d = new Directive({
+      name: 'test',
+      def: def,
+      expression: 'a'
+    }, vm, el)
+    d._bind()
+    expect(d.params.foo).toBe('hello')
+    expect(d.params.keBab).toBe('yo')
+  })
+
+  it('dynamic params', function (done) {
+    el.setAttribute(':foo', 'a')
+    el.setAttribute(':ke-bab', '123')
+    var d = new Directive({
+      name: 'test',
+      def: def,
+      expression: 'a'
+    }, vm, el)
+    d._bind()
+    expect(d.params.foo).toBe(vm.a)
+    expect(d.params.keBab).toBe(123)
+    vm.a = 2
+    nextTick(function () {
+      expect(def.paramWatchers.foo).toHaveBeenCalledWith(2, 1)
+      expect(d.params.foo).toBe(vm.a)
+      done()
+    })
+  })
+})
diff --git a/test/unit/specs/directives/element/partial_spec.js b/test/unit/specs/directives/element/partial_spec.js
new file mode 100644
index 00000000000..6733f5be479
--- /dev/null
+++ b/test/unit/specs/directives/element/partial_spec.js
@@ -0,0 +1,120 @@
+var Vue = require('src')
+var _ = require('src/util')
+var compiler = require('src/compiler')
+
+describe('Partial', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+  })
+
+  it('static', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<partial name="p"></partial>',
+      data: {
+        msg: 'foo'
+      },
+      partials: {
+        p: '{{msg}}'
+      }
+    })
+    expect(el.textContent).toBe('foo')
+    vm.msg = 'bar'
+    _.nextTick(function () {
+      expect(el.textContent).toBe('bar')
+      done()
+    })
+  })
+
+  it('dynamic', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<partial :name="\'test-\' + id"></partial>',
+      data: {
+        id: 'a'
+      },
+      partials: {
+        'test-a': 'a {{id}}',
+        'test-b': 'b {{id}}'
+      }
+    })
+    expect(el.textContent).toBe('a a')
+    vm.id = 'b'
+    _.nextTick(function () {
+      expect(el.textContent).toBe('b b')
+      done()
+    })
+  })
+
+  it('dynamic inside v-for', function () {
+    new Vue({
+      el: el,
+      template: '<div v-for="id in list"><partial v-bind:name="\'test-\' + id"></partial></div>',
+      data: {
+        list: ['foo', 'bar']
+      },
+      partials: {
+        'test-foo': 'foo {{id}}',
+        'test-bar': 'bar {{id}}'
+      }
+    })
+    expect(el.textContent).toBe('foo foobar bar')
+  })
+
+  it('caching', function () {
+    var calls = 0
+    var compile = compiler.compile
+    compiler.compile = function () {
+      calls++
+      return compile.apply(this, arguments)
+    }
+    // Note: caching only works on components, not native Vue
+    var Comp = Vue.extend({
+      template:
+        '<partial name="cache-test"></partial> ' +
+        '<partial name="cache-test"></partial>',
+      partials: {
+        'cache-test': 'foo {{msg}}'
+      }
+    })
+    new Comp({
+      el: el,
+      data: {
+        msg: 'bar'
+      }
+    })
+    expect(el.textContent).toBe('foo bar foo bar')
+    // one call for instance, and one for partial
+    expect(calls).toBe(2)
+    // cleanup
+    compiler.compile = compile
+  })
+
+  it('teardown', function () {
+    var vm = new Vue({
+      el: el,
+      template: '<partial :name="\'test-\' + id"></partial>',
+      data: {
+        id: 'a'
+      },
+      partials: {
+        'test-a': 'a {{id}}'
+      }
+    })
+    expect(vm._directives.length).toBe(2)
+    expect(vm._watchers.length).toBe(2)
+    var partialDir
+    vm._directives.some(function (d) {
+      if (d.name === 'partial') {
+        partialDir = d
+        return true
+      }
+    })
+    partialDir._teardown()
+    // the text-directive should've been removed.
+    expect(vm._directives.length).toBe(1)
+    expect(vm._directives[0].name).toBe('partial')
+    expect(vm._watchers.length).toBe(0)
+  })
+})
diff --git a/test/unit/specs/directives/element/slot_spec.js b/test/unit/specs/directives/element/slot_spec.js
new file mode 100644
index 00000000000..28c43e9f906
--- /dev/null
+++ b/test/unit/specs/directives/element/slot_spec.js
@@ -0,0 +1,479 @@
+var Vue = require('src')
+var nextTick = Vue.nextTick
+
+describe('Slot Distribution', function () {
+  var el, vm, options
+  beforeEach(function () {
+    el = document.createElement('div')
+    options = {
+      el: el,
+      data: {
+        msg: 'self'
+      }
+    }
+  })
+
+  function mount () {
+    vm = new Vue(options)
+  }
+
+  it('no content', function () {
+    options.template = '<div><slot></slot></div>'
+    mount()
+    expect(el.firstChild.childNodes.length).toBe(0)
+  })
+
+  it('default content', function () {
+    el.innerHTML = '<p>foo</p>'
+    options.template = '<div><slot></slot></div>'
+    mount()
+    expect(el.firstChild.tagName).toBe('DIV')
+    expect(el.firstChild.firstChild.tagName).toBe('P')
+    expect(el.firstChild.firstChild.textContent).toBe('foo')
+  })
+
+  it('no template auto content', function () {
+    el.innerHTML = '<p>foo</p>'
+    options._asComponent = true
+    mount()
+    expect(el.firstChild.tagName).toBe('P')
+    expect(el.firstChild.textContent).toBe('foo')
+  })
+
+  it('fallback content', function () {
+    options.template = '<slot><p>{{msg}}</p></slot>'
+    mount()
+    expect(el.firstChild.tagName).toBe('P')
+    expect(el.firstChild.textContent).toBe('self')
+  })
+
+  it('fallback content with multiple named slots', function () {
+    el.innerHTML = '<p slot="b">slot b</p>'
+    options.template =
+      '<slot name="a"><p>fallback a</p></slot>' +
+      '<slot name="b">fallback b</slot>'
+    mount()
+    expect(el.childNodes.length).toBe(2)
+    expect(el.firstChild.textContent).toBe('fallback a')
+    expect(el.lastChild.textContent).toBe('slot b')
+  })
+
+  it('fallback content with mixed named/unnamed slots', function () {
+    el.innerHTML = '<p slot="b">slot b</p>'
+    options.template =
+      '<slot><p>fallback a</p></slot>' +
+      '<slot name="b">fallback b</slot>'
+    mount()
+    expect(el.childNodes.length).toBe(2)
+    expect(el.firstChild.textContent).toBe('fallback a')
+    expect(el.lastChild.textContent).toBe('slot b')
+  })
+
+  it('selector matching multiple elements', function () {
+    el.innerHTML = '<p slot="t">1</p><div></div><p slot="t">2</p>'
+    options.template = '<slot name="t"></slot>'
+    mount()
+    expect(el.innerHTML).toBe('<p slot="t">1</p><p slot="t">2</p>')
+  })
+
+  it('default content should only render parts not selected', function () {
+    el.innerHTML = '<div>foo</div><p slot="a">1</p><p slot="b">2</p>'
+    options.template =
+      '<slot name="a"></slot>' +
+      '<slot></slot>' +
+      '<slot name="b"></slot>'
+    mount()
+    expect(el.innerHTML).toBe('<p slot="a">1</p><div>foo</div><p slot="b">2</p>')
+  })
+
+  it('content transclusion with replace', function () {
+    el.innerHTML = '<p>foo</p>'
+    options.template = '<div><div><slot></slot></div></div>'
+    options.replace = true
+    mount()
+    var res = vm.$el
+    expect(res).not.toBe(el)
+    expect(res.firstChild.tagName).toBe('DIV')
+    expect(res.firstChild.firstChild.tagName).toBe('P')
+    expect(res.firstChild.firstChild.textContent).toBe('foo')
+  })
+
+  it('block instance content transclusion', function () {
+    el.innerHTML = '<p slot="p">foo</p><span slot="span">ho</span>'
+    options.template = '<div></div><slot name="p"></slot><slot name="span"></slot>'
+    options.replace = true
+    mount()
+    expect(getChild(1).tagName).toBe('DIV')
+    expect(getChild(2).tagName).toBe('P')
+    expect(getChild(3).tagName).toBe('SPAN')
+
+    function getChild (n) {
+      var el = vm._fragmentStart
+      while (n--) {
+        el = el.nextSibling
+      }
+      return el
+    }
+  })
+
+  it('name should only match children', function () {
+    el.innerHTML =
+      '<p slot="b">select b</p>' +
+      '<span><p slot="b">nested b</p></span>' +
+      '<span><p slot="c">nested c</p></span>'
+    options.template =
+      '<slot name="a"><p>fallback a</p></slot>' +
+      '<slot name="b">fallback b</slot>' +
+      '<slot name="c">fallback c</slot>'
+    mount()
+    expect(el.childNodes.length).toBe(3)
+    expect(el.firstChild.textContent).toBe('fallback a')
+    expect(el.childNodes[1].textContent).toBe('select b')
+    expect(el.lastChild.textContent).toBe('fallback c')
+  })
+
+  it('should accept expressions in selectors', function () {
+    el.innerHTML = '<p>one</p><p slot="two">two</p>'
+    options.template = '<slot :name="theName"></slot>'
+    options.data = {
+      theName: 'two'
+    }
+    mount()
+    expect(el.innerHTML).toBe('<p slot="two">two</p>')
+  })
+
+  it('content should be dynamic and compiled in parent scope', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        msg: 'foo'
+      },
+      template: '<test>{{msg}}</test>',
+      components: {
+        test: {
+          template: '<slot></slot>'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<test>foo</test>')
+    vm.msg = 'bar'
+    nextTick(function () {
+      expect(el.innerHTML).toBe('<test>bar</test>')
+      done()
+    })
+  })
+
+  it('v-if with content transclusion', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        a: 1,
+        b: 2,
+        show: true
+      },
+      template: '<test :show="show"><p slot="b">{{b}}</a><p>{{a}}</p></test>',
+      components: {
+        test: {
+          props: ['show'],
+          template: '<div v-if="show"><slot></slot><slot name="b"></slot></div>'
+        }
+      }
+    })
+    expect(el.textContent).toBe('12')
+    vm.a = 2
+    nextTick(function () {
+      expect(el.textContent).toBe('22')
+      vm.show = false
+      nextTick(function () {
+        expect(el.textContent).toBe('')
+        vm.show = true
+        vm.a = 3
+        nextTick(function () {
+          expect(el.textContent).toBe('32')
+          done()
+        })
+      })
+    })
+  })
+
+  it('inline v-for', function () {
+    el.innerHTML = '<p slot="1">1</p><p slot="2">2</p><p slot="3">3</p>'
+    new Vue({
+      el: el,
+      template: '<div v-for="n in list"><slot :name="$index + 1"></slot></div>',
+      data: {
+        list: 0
+      },
+      beforeCompile: function () {
+        this.list = this.$options._content.querySelectorAll('p').length
+      }
+    })
+    expect(el.innerHTML).toBe('<div><p slot="1">1</p></div><div><p slot="2">2</p></div><div><p slot="3">3</p></div>')
+  })
+
+  it('v-for + component + parent directive + transclusion', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<test v-for="n in list" :class="cls" :a="n.a">{{msg}}</test>',
+      data: {
+        cls: 'parent',
+        msg: 'foo',
+        list: [{a: 1}, {a: 2}, {a: 3}]
+      },
+      components: {
+        test: {
+          replace: true,
+          props: ['a'],
+          template: '<div class="child">{{a}} <slot></slot></div>'
+        }
+      }
+    })
+    var markup = vm.list.map(function (item) {
+      return '<div class="child parent">' + item.a + ' foo</div>'
+    }).join('')
+    expect(el.innerHTML).toBe(markup)
+    vm.msg = 'bar'
+    markup = vm.list.map(function (item) {
+      return '<div class="child parent">' + item.a + ' bar</div>'
+    }).join('')
+    nextTick(function () {
+      expect(el.innerHTML).toBe(markup)
+      done()
+    })
+  })
+
+  it('nested transclusions', function (done) {
+    vm = new Vue({
+      el: el,
+      template:
+        '<testa>' +
+          '<testb>' +
+            '<div v-for="n in list">{{n}}</div>' +
+          '</testb>' +
+        '</testa>',
+      data: {
+        list: [1, 2]
+      },
+      components: {
+        testa: { template: '<slot></slot>' },
+        testb: { template: '<slot></slot>' }
+      }
+    })
+    expect(el.innerHTML).toBe(
+      '<testa><testb>' +
+        '<div>1</div><div>2</div>' +
+      '</testb></testa>'
+    )
+    vm.list.push(3)
+    nextTick(function () {
+      expect(el.innerHTML).toBe(
+        '<testa><testb>' +
+          '<div>1</div><div>2</div><div>3</div>' +
+        '</testb></testa>'
+      )
+      done()
+    })
+  })
+
+  it('nested transclusion, container dirs & props', function (done) {
+    vm = new Vue({
+      el: el,
+      template:
+        '<testa>' +
+          '<testb v-if="ok" :msg="msg"></testb>' +
+        '</testa>',
+      data: {
+        ok: false,
+        msg: 'hello'
+      },
+      components: {
+        testa: { template: '<slot></slot>' },
+        testb: {
+          props: ['msg'],
+          template: '{{msg}}'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<testa></testa>')
+    vm.ok = true
+    nextTick(function () {
+      expect(el.innerHTML).toBe('<testa><testb>hello</testb></testa>')
+      done()
+    })
+  })
+
+  // #1010
+  it('v-for inside transcluded content', function () {
+    vm = new Vue({
+      el: el,
+      template:
+        '<testa>' +
+          '{{inner}} {{outer}}' +
+          '<div v-for="item in list"> {{item.inner}} {{outer}}</div>' +
+        '</testa>',
+      data: {
+        outer: 'outer',
+        inner: 'parent-inner',
+        list: [
+          { inner: 'list-inner' }
+        ]
+      },
+      components: {
+        testa: {
+          data: function () {
+            return {
+              inner: 'component-inner'
+            }
+          },
+          template: '<slot></slot>'
+        }
+      }
+    })
+    expect(el.textContent).toBe('parent-inner outer list-inner outer')
+  })
+
+  it('single content outlet with replace: true', function () {
+    vm = new Vue({
+      el: el,
+      template:
+        '<test><p>1</p><p>2</p></test>',
+      components: {
+        test: {
+          template: '<slot></slot>',
+          replace: true
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<p>1</p><p>2</p>')
+  })
+
+  it('template slot', function () {
+    vm = new Vue({
+      el: el,
+      template:
+        '<test><template slot="test">hello</template></test>',
+      components: {
+        test: {
+          template: '<slot name="test"></slot> world',
+          replace: true
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('hello world')
+  })
+
+  it('inside v-for', function () {
+    new Vue({
+      el: el,
+      template: '<comp v-for="item in items">{{item.value}}</comp>',
+      data: {
+        items: [{value: 123}, {value: 234}]
+      },
+      components: {
+        comp: {
+          tempalte: '<div><slot></slot></div>'
+        }
+      }
+    })
+    expect(el.textContent).toBe('123234')
+  })
+
+  it('fallback inside v-for', function () {
+    new Vue({
+      el: el,
+      template: '<div v-for="n in 3"><comp></comp></div>',
+      components: {
+        comp: {
+          template: '<div><slot>{{foo}}</slot></div>',
+          data: function () {
+            return {
+              foo: 'bar'
+            }
+          }
+        }
+      }
+    })
+    expect(el.textContent).toBe('barbarbar')
+  })
+
+  it('fallback for slot with v-if', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        ok: false,
+        msg: 'inserted'
+      },
+      template: '<div><comp><div v-if="ok">{{ msg }}</div></comp></div>',
+      components: {
+        comp: {
+          data: function () {
+            return { msg: 'fallback' }
+          },
+          template: '<div><slot>{{ msg }}</slot></div>'
+        }
+      }
+    })
+    expect(el.textContent).toBe('fallback')
+    vm.ok = true
+    nextTick(function () {
+      expect(el.textContent).toBe('inserted')
+      done()
+    })
+  })
+
+  // #2435
+  it('slot inside template', function () {
+    var vm = new Vue({
+      el: el,
+      template: '<test>foo</test>',
+      components: {
+        test: {
+          data: function () {
+            return { ok: true }
+          },
+          template:
+            '<div>' +
+              '<template v-if="ok">' +
+                '<template v-if="ok">' +
+                  '<slot>{{ msg }}</slot>' +
+                '</template>' +
+              '</template>' +
+            '</div>'
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('foo')
+  })
+
+  it('warn dynamic slot attribute', function () {
+    new Vue({
+      el: el,
+      template: '<test><div :slot="1"></div></test>',
+      components: {
+        test: {
+          template: '<div><slot></slot></div>'
+        }
+      }
+    })
+    expect('"slot" attribute must be static').toHaveBeenWarned()
+  })
+
+  it('default slot should use fallback content if has only whitespace', function () {
+    new Vue({
+      el: el,
+      template: '<test><div slot="first">1</div> <div slot="second">2</div></test>',
+      components: {
+        test: {
+          replace: true,
+          template:
+            '<div class="wrapper">' +
+              '<slot name="first"><p>first slot</p></slot>' +
+              '<slot><p>this is the default slot</p></slot>' +
+              '<slot name="second"><p>second named slot</p></slot>' +
+            '</div>'
+        }
+      }
+    })
+    expect(el.children[0].innerHTML).toBe('<div slot="first">1</div><p>this is the default slot</p><div slot="second">2</div>')
+  })
+})
diff --git a/test/unit/specs/directives/internal/class_spec.js b/test/unit/specs/directives/internal/class_spec.js
new file mode 100644
index 00000000000..44e3858d291
--- /dev/null
+++ b/test/unit/specs/directives/internal/class_spec.js
@@ -0,0 +1,82 @@
+var _ = require('src/util')
+var def = require('src/directives/internal/class')
+
+describe(':class', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+  })
+
+  it('plain string', function () {
+    el.className = 'foo'
+    var dir = _.extend({ el: el }, def)
+    dir.update('bar')
+    expect(el.className).toBe('foo bar')
+    dir.update('baz qux')
+    expect(el.className).toBe('foo baz qux')
+    dir.update('qux')
+    expect(el.className).toBe('foo qux')
+    dir.update()
+    expect(el.className).toBe('foo')
+  })
+
+  it('object value', function () {
+    el.className = 'foo'
+    var dir = _.extend({ el: el }, def)
+    dir.update({
+      bar: true,
+      baz: false
+    })
+    expect(el.className).toBe('foo bar')
+    dir.update({
+      baz: true
+    })
+    expect(el.className).toBe('foo baz')
+    dir.update(null)
+    expect(el.className).toBe('foo')
+
+    dir.update({
+      'bar baz': true,
+      qux: false
+    })
+    expect(el.className).toBe('foo bar baz')
+    dir.update({
+      qux: true
+    })
+    expect(el.className).toBe('foo qux')
+  })
+
+  it('array value', function () {
+    el.className = 'a'
+    var dir = _.extend({ el: el }, def)
+    dir.update(['b', 'c'])
+    expect(el.className).toBe('a b c')
+    dir.update(['d', 'c'])
+    expect(el.className).toBe('a c d')
+    dir.update(['w', 'x y z'])
+    expect(el.className).toBe('a w x y z')
+    dir.update()
+    expect(el.className).toBe('a')
+    // test mutating array
+    var arr = ['e', '']
+    dir.update(arr)
+    expect(el.className).toBe('a e')
+    arr.length = 0
+    arr.push('f')
+    dir.update(arr)
+    expect(el.className).toBe('a f')
+    // test array with objects
+    dir.update(['x', { y: true, z: true }])
+    expect(el.className).toBe('a x y z')
+    dir.update(['x', { y: true, z: false }])
+    expect(el.className).toBe('a x y')
+    dir.update(['f', { z: true }])
+    expect(el.className).toBe('a f z')
+    dir.update(['l', 'f', { n: true, z: true }])
+    expect(el.className).toBe('a f z l n')
+    dir.update(['x', {}])
+    expect(el.className).toBe('a x')
+    dir.update()
+    expect(el.className).toBe('a')
+  })
+})
diff --git a/test/unit/specs/directives/internal/component_spec.js b/test/unit/specs/directives/internal/component_spec.js
new file mode 100644
index 00000000000..62ab5d60ac2
--- /dev/null
+++ b/test/unit/specs/directives/internal/component_spec.js
@@ -0,0 +1,565 @@
+var _ = require('src/util')
+var Vue = require('src')
+
+describe('Component', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+    document.body.appendChild(el)
+  })
+
+  afterEach(function () {
+    document.body.removeChild(el)
+  })
+
+  it('static', function () {
+    new Vue({
+      el: el,
+      template: '<test></test>',
+      components: {
+        test: {
+          data: function () {
+            return { a: 123 }
+          },
+          template: '{{a}}'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<test>123</test>')
+  })
+
+  it('replace', function () {
+    new Vue({
+      el: el,
+      template: '<test></test>',
+      components: {
+        test: {
+          replace: true,
+          data: function () {
+            return { a: 123 }
+          },
+          template: '<p>{{a}}</p>'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<p>123</p>')
+  })
+
+  it('"is" on table elements', function () {
+    var vm = new Vue({
+      el: el,
+      template: '<table><tbody><tr is="test"></tr></tbody></table>',
+      components: {
+        test: {
+          data: function () {
+            return { a: 123 }
+          },
+          template: '<td>{{a}}</td>'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe(vm.$options.template.replace(/<tr.*\/tr>/, '<tr><td>123</td></tr>'))
+    expect(getWarnCount()).toBe(0)
+  })
+
+  it('inline-template', function () {
+    new Vue({
+      el: el,
+      template: '<test inline-template>{{a}}</test>',
+      data: {
+        a: 'parent'
+      },
+      components: {
+        test: {
+          data: function () {
+            return { a: 'child' }
+          },
+          template: 'child option template'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<test>child</test>')
+  })
+
+  it('block replace', function () {
+    new Vue({
+      el: el,
+      template: '<test></test>',
+      components: {
+        test: {
+          replace: true,
+          data: function () {
+            return { a: 123, b: 234 }
+          },
+          template: '<p>{{a}}</p><p>{{b}}</p>'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<p>123</p><p>234</p>')
+  })
+
+  it('dynamic', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<component :is="view" :view="view"></component>',
+      data: {
+        view: 'view-a'
+      },
+      components: {
+        'view-a': {
+          template: '<div>foo</div>',
+          replace: true,
+          data: function () {
+            return { view: 'a' }
+          }
+        },
+        'view-b': {
+          template: '<div>bar</div>',
+          replace: true,
+          data: function () {
+            return { view: 'b' }
+          }
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<div view="view-a">foo</div>')
+    vm.view = 'view-b'
+    _.nextTick(function () {
+      expect(el.innerHTML).toBe('<div view="view-b">bar</div>')
+      vm.view = ''
+      _.nextTick(function () {
+        expect(el.innerHTML).toBe('')
+        done()
+      })
+    })
+  })
+
+  it(':is using raw component constructor', function () {
+    new Vue({
+      el: el,
+      template:
+        '<component :is="$options.components.test"></component>' +
+        '<component :is="$options.components.async"></component>',
+      components: {
+        test: {
+          template: 'foo'
+        },
+        async: function (resolve) {
+          resolve({
+            template: 'bar'
+          })
+        }
+      }
+    })
+    expect(el.textContent).toBe('foobar')
+  })
+
+  it('keep-alive', function (done) {
+    var spyA = jasmine.createSpy()
+    var spyB = jasmine.createSpy()
+    var vm = new Vue({
+      el: el,
+      template: '<component :is="view" keep-alive></component>',
+      data: {
+        view: 'view-a'
+      },
+      components: {
+        'view-a': {
+          created: spyA,
+          template: '<div>foo</div>',
+          replace: true
+        },
+        'view-b': {
+          created: spyB,
+          template: '<div>bar</div>',
+          replace: true
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<div>foo</div>')
+    expect(spyA.calls.count()).toBe(1)
+    expect(spyB.calls.count()).toBe(0)
+    vm.view = 'view-b'
+    _.nextTick(function () {
+      expect(el.innerHTML).toBe('<div>bar</div>')
+      expect(spyA.calls.count()).toBe(1)
+      expect(spyB.calls.count()).toBe(1)
+      vm.view = 'view-a'
+      _.nextTick(function () {
+        expect(el.innerHTML).toBe('<div>foo</div>')
+        expect(spyA.calls.count()).toBe(1)
+        expect(spyB.calls.count()).toBe(1)
+        vm.view = 'view-b'
+        _.nextTick(function () {
+          expect(el.innerHTML).toBe('<div>bar</div>')
+          expect(spyA.calls.count()).toBe(1)
+          expect(spyB.calls.count()).toBe(1)
+          done()
+        })
+      })
+    })
+  })
+
+  it('should compile parent template directives & content in parent scope', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        ok: false,
+        message: 'hello'
+      },
+      template: '<test v-show="ok">{{message}}</test>',
+      components: {
+        test: {
+          template: '<div><slot></slot> {{message}}</div>',
+          replace: true,
+          data: function () {
+            return {
+              message: 'world'
+            }
+          }
+        }
+      }
+    })
+    expect(el.firstChild.style.display).toBe('none')
+    expect(el.firstChild.textContent).toBe('hello world')
+    vm.ok = true
+    vm.message = 'bye'
+    _.nextTick(function () {
+      expect(el.firstChild.style.display).toBe('')
+      expect(el.firstChild.textContent).toBe('bye world')
+      done()
+    })
+  })
+
+  it('parent content + v-if', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        ok: false,
+        message: 'hello'
+      },
+      template: '<test v-if="ok">{{message}}</test>',
+      components: {
+        test: {
+          template: '<slot></slot> {{message}}',
+          data: function () {
+            return {
+              message: 'world'
+            }
+          }
+        }
+      }
+    })
+    expect(el.textContent).toBe('')
+    expect(vm.$children.length).toBe(0)
+    expect(vm._directives.length).toBe(1) // v-if
+    vm.ok = true
+    _.nextTick(function () {
+      expect(vm.$children.length).toBe(1)
+      expect(vm._directives.length).toBe(3) // v-if, component, v-text
+      expect(el.textContent).toBe('hello world')
+      done()
+    })
+  })
+
+  it('props', function () {
+    new Vue({
+      el: el,
+      data: {
+        list: [{a: 1}, {a: 2}]
+      },
+      template: '<test :collection="list"></test>',
+      components: {
+        test: {
+          template: '<ul><li v-for="item in collection">{{item.a}}</li></ul>',
+          replace: true,
+          props: ['collection']
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<ul><li>1</li><li>2</li></ul>')
+  })
+
+  it('activate hook for static component', function (done) {
+    new Vue({
+      el: el,
+      template: '<view-a></view-a>',
+      components: {
+        'view-a': {
+          template: 'foo',
+          activate: function (ready) {
+            setTimeout(function () {
+              expect(el.textContent).toBe('')
+              ready()
+              expect(el.textContent).toBe('foo')
+              done()
+            }, 0)
+          }
+        }
+      }
+    })
+  })
+
+  it('multiple activate hooks', function (done) {
+    var mixinSpy = jasmine.createSpy('mixin activate')
+    new Vue({
+      el: el,
+      template: '<view-a></view-a>',
+      components: {
+        'view-a': {
+          template: 'foo',
+          mixins: [{
+            activate: function (done) {
+              expect(el.textContent).toBe('')
+              mixinSpy()
+              done()
+            }
+          }],
+          activate: function (ready) {
+            setTimeout(function () {
+              expect(mixinSpy).toHaveBeenCalled()
+              expect(el.textContent).toBe('')
+              ready()
+              expect(el.textContent).toBe('foo')
+              done()
+            }, 0)
+          }
+        }
+      }
+    })
+  })
+
+  it('activate hook for dynamic components', function (done) {
+    var next
+    var vm = new Vue({
+      el: el,
+      data: {
+        view: 'view-a'
+      },
+      template: '<component :is="view"></component>',
+      components: {
+        'view-a': {
+          template: 'foo',
+          activate: function (ready) {
+            next = ready
+          }
+        },
+        'view-b': {
+          template: 'bar',
+          activate: function (ready) {
+            next = ready
+          }
+        }
+      }
+    })
+    expect(next).toBeTruthy()
+    expect(el.textContent).toBe('')
+    next()
+    expect(el.textContent).toBe('foo')
+    vm.view = 'view-b'
+    _.nextTick(function () {
+      expect(el.textContent).toBe('foo')
+      // old vm is already removed, this is the new vm
+      expect(vm.$children.length).toBe(1)
+      next()
+      expect(el.textContent).toBe('bar')
+      // ensure switching before ready event correctly
+      // cleans up the component being waited on.
+      // see #1152
+      vm.view = 'view-a'
+      // store the ready callback for view-a
+      var callback = next
+      _.nextTick(function () {
+        vm.view = 'view-b'
+        _.nextTick(function () {
+          expect(vm.$children.length).toBe(1)
+          expect(vm.$children[0].$el.textContent).toBe('bar')
+          // calling view-a's ready callback here should not throw
+          // because it should've been cancelled (#1994)
+          expect(callback).not.toThrow()
+          done()
+        })
+      })
+    })
+  })
+
+  it('activate hook + keep-alive', function (done) {
+    var next
+    var vm = new Vue({
+      el: el,
+      data: {
+        view: 'view-a'
+      },
+      template: '<component :is="view" keep-alive></component>',
+      components: {
+        'view-a': {
+          template: 'foo',
+          activate: function (ready) {
+            next = ready
+          }
+        },
+        'view-b': {
+          template: 'bar',
+          activate: function (ready) {
+            next = ready
+          }
+        }
+      }
+    })
+    next()
+    expect(el.textContent).toBe('foo')
+    vm.view = 'view-b'
+    _.nextTick(function () {
+      expect(vm.$children.length).toBe(2)
+      next()
+      expect(el.textContent).toBe('bar')
+      vm.view = 'view-a'
+      _.nextTick(function () {
+        // should switch without the need to emit
+        // because of keep-alive
+        expect(el.textContent).toBe('foo')
+        done()
+      })
+    })
+  })
+
+  it('transition-mode: in-out', function (done) {
+    var spy1 = jasmine.createSpy('enter')
+    var spy2 = jasmine.createSpy('leave')
+    var next
+    var vm = new Vue({
+      el: el,
+      data: {
+        view: 'view-a'
+      },
+      template: '<component :is="view" transition="test" transition-mode="in-out"></component>',
+      components: {
+        'view-a': { template: 'foo' },
+        'view-b': { template: 'bar' }
+      },
+      transitions: {
+        test: {
+          enter: function (el, done) {
+            spy1()
+            next = done
+          },
+          leave: function (el, done) {
+            spy2()
+            _.nextTick(done)
+          }
+        }
+      }
+    })
+    expect(el.textContent).toBe('foo')
+    vm.view = 'view-b'
+    _.nextTick(function () {
+      expect(spy1).toHaveBeenCalled()
+      expect(spy2).not.toHaveBeenCalled()
+      expect(el.textContent).toBe('foobar')
+      next()
+      _.nextTick(function () {
+        expect(spy2).toHaveBeenCalled()
+        _.nextTick(function () {
+          expect(el.textContent).toBe('bar')
+          done()
+        })
+      })
+    })
+  })
+
+  it('transition-mode: out-in', function (done) {
+    var spy1 = jasmine.createSpy('enter')
+    var spy2 = jasmine.createSpy('leave')
+    var next
+    var vm = new Vue({
+      el: el,
+      data: {
+        view: 'view-a'
+      },
+      template: '<component :is="view" transition="test" transition-mode="out-in"></component>',
+      components: {
+        'view-a': { template: 'foo' },
+        'view-b': { template: 'bar' }
+      },
+      transitions: {
+        test: {
+          enter: function (el, done) {
+            spy2()
+            _.nextTick(done)
+          },
+          leave: function (el, done) {
+            spy1()
+            next = done
+          }
+        }
+      }
+    })
+    expect(el.textContent).toBe('foo')
+    vm.view = 'view-b'
+    _.nextTick(function () {
+      expect(spy1).toHaveBeenCalled()
+      expect(spy2).not.toHaveBeenCalled()
+      expect(el.textContent).toBe('foo')
+      next()
+      expect(spy2).toHaveBeenCalled()
+      expect(el.textContent).toBe('bar')
+      done()
+    })
+  })
+
+  it('teardown', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<component :is="view" keep-alive></component>',
+      data: {
+        view: 'test'
+      },
+      components: {
+        test: {},
+        test2: {}
+      }
+    })
+    vm.view = 'test2'
+    _.nextTick(function () {
+      expect(vm.$children.length).toBe(2)
+      var child = vm.$children[0]
+      var child2 = vm.$children[1]
+      vm._directives[0].unbind()
+      expect(vm._directives[0].cache).toBeNull()
+      expect(vm.$children.length).toBe(0)
+      expect(child._isDestroyed).toBe(true)
+      expect(child2._isDestroyed).toBe(true)
+      done()
+    })
+  })
+
+  it('already mounted warn', function () {
+    new Vue({
+      el: document.createElement('test'),
+      components: {
+        test: {}
+      }
+    })
+    expect('cannot mount component "test" on already mounted element').toHaveBeenWarned()
+  })
+
+  it('not found component should not throw', function () {
+    expect(function () {
+      new Vue({
+        el: el,
+        template: '<div is="non-existent"></div>'
+      })
+    }).not.toThrow()
+  })
+
+  it('warn possible camelCase components', function () {
+    new Vue({
+      el: document.createElement('div'),
+      template: '<HelloWorld></HelloWorld>',
+      components: {
+        'hello-world': {}
+      }
+    })
+    expect('did you mean <hello-world>?').toHaveBeenWarned()
+  })
+})
diff --git a/test/unit/specs/directives/internal/prop_spec.js b/test/unit/specs/directives/internal/prop_spec.js
new file mode 100644
index 00000000000..084e2eeebf8
--- /dev/null
+++ b/test/unit/specs/directives/internal/prop_spec.js
@@ -0,0 +1,924 @@
+var Vue = require('src')
+
+describe('prop', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+  })
+
+  it('one way binding', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        b: 'bar'
+      },
+      template: '<test v-bind:b="b" v-ref:child></test>',
+      components: {
+        test: {
+          props: ['b'],
+          template: '{{b}}'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<test>bar</test>')
+    vm.b = 'baz'
+    Vue.nextTick(function () {
+      expect(el.innerHTML).toBe('<test>baz</test>')
+      vm.$refs.child.b = 'qux'
+      expect(vm.b).toBe('baz')
+      Vue.nextTick(function () {
+        expect(el.innerHTML).toBe('<test>qux</test>')
+        done()
+      })
+    })
+  })
+
+  it('with filters', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<test :name="a | test"></test>',
+      data: {
+        a: 123
+      },
+      filters: {
+        test: function (v) {
+          return v * 2
+        }
+      },
+      components: {
+        test: {
+          props: ['name'],
+          template: '{{name}}'
+        }
+      }
+    })
+    expect(el.textContent).toBe('246')
+    vm.a = 234
+    Vue.nextTick(function () {
+      expect(el.textContent).toBe('468')
+      done()
+    })
+  })
+
+  it('two-way binding', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        b: 'B',
+        test: {
+          a: 'A'
+        }
+      },
+      template: '<test v-bind:testt.sync="test" :bb.sync="b" :a.sync=" test.a " v-ref:child></test>',
+      components: {
+        test: {
+          props: ['testt', 'bb', 'a'],
+          template: '{{testt.a}} {{bb}} {{a}}'
+        }
+      }
+    })
+    expect(el.firstChild.textContent).toBe('A B A')
+    vm.test.a = 'AA'
+    vm.b = 'BB'
+    Vue.nextTick(function () {
+      expect(el.firstChild.textContent).toBe('AA BB AA')
+      vm.test = { a: 'foo' }
+      Vue.nextTick(function () {
+        expect(el.firstChild.textContent).toBe('foo BB foo')
+        vm.$data = {
+          b: 'bar',
+          test: {
+            a: 'fooA'
+          }
+        }
+        Vue.nextTick(function () {
+          expect(el.firstChild.textContent).toBe('fooA bar fooA')
+          // test two-way
+          vm.$refs.child.bb = 'B'
+          vm.$refs.child.testt = { a: 'A' }
+          Vue.nextTick(function () {
+            expect(el.firstChild.textContent).toBe('A B A')
+            expect(vm.test.a).toBe('A')
+            expect(vm.test).toBe(vm.$refs.child.testt)
+            expect(vm.b).toBe('B')
+            vm.$refs.child.a = 'Oops'
+            Vue.nextTick(function () {
+              expect(el.firstChild.textContent).toBe('Oops B Oops')
+              expect(vm.test.a).toBe('Oops')
+              done()
+            })
+          })
+        })
+      })
+    })
+  })
+
+  it('explicit one time binding', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        b: 'foo'
+      },
+      template: '<test :b.once="b" v-ref:child></test>',
+      components: {
+        test: {
+          props: ['b'],
+          template: '{{b}}'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<test>foo</test>')
+    vm.b = 'bar'
+    Vue.nextTick(function () {
+      expect(el.innerHTML).toBe('<test>foo</test>')
+      done()
+    })
+  })
+
+  it('warn non-settable parent path', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        b: 'foo'
+      },
+      template: '<test :b.sync=" b + \'bar\'" v-ref:child></test>',
+      components: {
+        test: {
+          props: ['b'],
+          template: '{{b}}'
+        }
+      }
+    })
+    expect('Cannot bind two-way prop with non-settable parent path').toHaveBeenWarned()
+    expect(el.innerHTML).toBe('<test>foobar</test>')
+    vm.b = 'baz'
+    Vue.nextTick(function () {
+      expect(el.innerHTML).toBe('<test>bazbar</test>')
+      vm.$refs.child.b = 'qux'
+      Vue.nextTick(function () {
+        expect(vm.b).toBe('baz')
+        expect(el.innerHTML).toBe('<test>qux</test>')
+        done()
+      })
+    })
+  })
+
+  it('warn expect two-way', function () {
+    new Vue({
+      el: el,
+      template: '<test :test="foo"></test>',
+      data: {
+        foo: 'bar'
+      },
+      components: {
+        test: {
+          props: {
+            test: {
+              twoWay: true
+            }
+          }
+        }
+      }
+    })
+    expect('expects a two-way binding type').toHaveBeenWarned()
+  })
+
+  it('warn $data as prop', function () {
+    new Vue({
+      el: el,
+      template: '<test></test>',
+      data: {
+        foo: 'bar'
+      },
+      components: {
+        test: {
+          props: ['$data']
+        }
+      }
+    })
+    expect('Do not use $data as prop').toHaveBeenWarned()
+  })
+
+  it('warn invalid keys', function () {
+    new Vue({
+      el: el,
+      template: '<test :a.b.c="test"></test>',
+      components: {
+        test: {
+          props: ['a.b.c']
+        }
+      }
+    })
+    expect('Invalid prop key').toHaveBeenWarned()
+  })
+
+  it('warn props with no el option', function () {
+    new Vue({
+      props: ['a']
+    })
+    expect('Props will not be compiled if no `el`').toHaveBeenWarned()
+  })
+
+  it('warn object/array default values', function () {
+    new Vue({
+      el: el,
+      props: {
+        arr: {
+          type: Array,
+          default: []
+        },
+        obj: {
+          type: Object,
+          default: {}
+        }
+      }
+    })
+    expect('use a factory function to return the default value').toHaveBeenWarned()
+    expect(getWarnCount()).toBe(2)
+  })
+
+  it('teardown', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        a: 'A',
+        b: 'B'
+      },
+      template: '<test :aa.sync="a" :bb="b"></test>',
+      components: {
+        test: {
+          props: ['aa', 'bb'],
+          template: '{{aa}} {{bb}}'
+        }
+      }
+    })
+    var child = vm.$children[0]
+    expect(el.firstChild.textContent).toBe('A B')
+    child.aa = 'AA'
+    vm.b = 'BB'
+    Vue.nextTick(function () {
+      expect(el.firstChild.textContent).toBe('AA BB')
+      expect(vm.a).toBe('AA')
+      // unbind the two props
+      child._directives[0].unbind()
+      child._directives[1].unbind()
+      child.aa = 'foo'
+      vm.b = 'BBB'
+      Vue.nextTick(function () {
+        expect(el.firstChild.textContent).toBe('foo BB')
+        expect(vm.a).toBe('AA')
+        done()
+      })
+    })
+  })
+
+  it('block instance with replace:true', function () {
+    new Vue({
+      el: el,
+      template: '<test :b="a" :c="d"></test>',
+      data: {
+        a: 'foo',
+        d: 'bar'
+      },
+      components: {
+        test: {
+          props: ['b', 'c'],
+          template: '<p>{{b}}</p><p>{{c}}</p>',
+          replace: true
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<p>foo</p><p>bar</p>')
+  })
+
+  describe('assertions', function () {
+    function makeInstance (value, type, validator, coerce, required) {
+      return new Vue({
+        el: document.createElement('div'),
+        template: '<test :test="val"></test>',
+        data: {
+          val: value
+        },
+        components: {
+          test: {
+            props: {
+              test: {
+                type: type,
+                validator: validator,
+                coerce: coerce,
+                required: required
+              }
+            }
+          }
+        }
+      })
+    }
+
+    it('string', function () {
+      makeInstance('hello', String)
+      expect(getWarnCount()).toBe(0)
+      makeInstance(123, String)
+      expect('Expected String').toHaveBeenWarned()
+    })
+
+    it('number', function () {
+      makeInstance(123, Number)
+      expect(getWarnCount()).toBe(0)
+      makeInstance('123', Number)
+      expect('Expected Number').toHaveBeenWarned()
+    })
+
+    it('boolean', function () {
+      makeInstance(true, Boolean)
+      expect(getWarnCount()).toBe(0)
+      makeInstance('123', Boolean)
+      expect('Expected Boolean').toHaveBeenWarned()
+    })
+
+    it('function', function () {
+      makeInstance(function () {}, Function)
+      expect(getWarnCount()).toBe(0)
+      makeInstance(123, Function)
+      expect('Expected Function').toHaveBeenWarned()
+    })
+
+    it('object', function () {
+      makeInstance({}, Object)
+      expect(getWarnCount()).toBe(0)
+      makeInstance([], Object)
+      expect('Expected Object').toHaveBeenWarned()
+    })
+
+    it('array', function () {
+      makeInstance([], Array)
+      expect(getWarnCount()).toBe(0)
+      makeInstance({}, Array)
+      expect('Expected Array').toHaveBeenWarned()
+    })
+
+    it('custom constructor', function () {
+      function Class () {}
+      makeInstance(new Class(), Class)
+      expect(getWarnCount()).toBe(0)
+      makeInstance({}, Class)
+      expect('Expected custom type').toHaveBeenWarned()
+    })
+
+    it('multiple types', function () {
+      makeInstance([], [Array, Number, Boolean])
+      expect(getWarnCount()).toBe(0)
+      makeInstance({}, [Array, Number, Boolean])
+      expect('Expected Array, Number, Boolean').toHaveBeenWarned()
+    })
+
+    it('custom validator', function () {
+      makeInstance(123, null, function (v) {
+        return v === 123
+      })
+      expect(getWarnCount()).toBe(0)
+      makeInstance(123, null, function (v) {
+        return v === 234
+      })
+      expect('custom validator check failed').toHaveBeenWarned()
+    })
+
+    it('type check + custom validator', function () {
+      makeInstance(123, Number, function (v) {
+        return v === 123
+      })
+      expect(getWarnCount()).toBe(0)
+      makeInstance(123, Number, function (v) {
+        return v === 234
+      })
+      expect('custom validator check failed').toHaveBeenWarned()
+      makeInstance(123, String, function (v) {
+        return v === 123
+      })
+      expect('Expected String').toHaveBeenWarned()
+    })
+
+    it('multiple types + custom validator', function () {
+      makeInstance(123, [Number, String, Boolean], function (v) {
+        return v === 123
+      })
+      expect(getWarnCount()).toBe(0)
+      makeInstance(123, [Number, String, Boolean], function (v) {
+        return v === 234
+      })
+      expect('custom validator check failed').toHaveBeenWarned()
+      makeInstance(123, [String, Boolean], function (v) {
+        return v === 123
+      })
+      expect('Expected String, Boolean').toHaveBeenWarned()
+    })
+
+    it('type check + coerce', function () {
+      makeInstance(123, String, null, String)
+      expect(getWarnCount()).toBe(0)
+      makeInstance('123', Number, null, Number)
+      expect(getWarnCount()).toBe(0)
+      makeInstance('123', Boolean, null, function (val) {
+        return val === '123'
+      })
+      expect(getWarnCount()).toBe(0)
+    })
+
+    it('warn if coerce is not a function', function () {
+      var coerce = 1
+      makeInstance('123', String, null, coerce)
+      expect(getWarnCount()).toBe(1)
+    })
+
+    it('multiple types + custom validator', function () {
+      makeInstance(123, [String, Boolean, Number], null, String)
+      expect(getWarnCount()).toBe(0)
+      makeInstance('123', [String, Boolean, Number], null, Number)
+      expect(getWarnCount()).toBe(0)
+      makeInstance('123', [String, Boolean, Function], null, function (val) {
+        return val === '123'
+      })
+      expect(getWarnCount()).toBe(0)
+    })
+
+    it('required', function () {
+      new Vue({
+        el: document.createElement('div'),
+        template: '<test></test>',
+        components: {
+          test: {
+            props: {
+              prop: { required: true }
+            }
+          }
+        }
+      })
+      expect('Missing required prop').toHaveBeenWarned()
+    })
+
+    it('optional with type + null/undefined', function () {
+      makeInstance(undefined, String)
+      expect(getWarnCount()).toBe(0)
+      makeInstance(null, String)
+      expect(getWarnCount()).toBe(0)
+    })
+
+    it('required with type + null/undefined', function () {
+      makeInstance(undefined, String, null, null, true)
+      expect(getWarnCount()).toBe(1)
+      expect('Expected String').toHaveBeenWarned()
+      makeInstance(null, Boolean, null, null, true)
+      expect(getWarnCount()).toBe(2)
+      expect('Expected Boolean').toHaveBeenWarned()
+    })
+  })
+
+  it('alternative syntax', function () {
+    new Vue({
+      el: el,
+      template: '<test :b="a" :c="d"></test>',
+      data: {
+        a: 'foo',
+        d: 'bar'
+      },
+      components: {
+        test: {
+          props: {
+            b: String,
+            c: {
+              type: Number
+            },
+            d: {
+              required: true
+            }
+          },
+          template: '<p>{{b}}</p><p>{{c}}</p>'
+        }
+      }
+    })
+    expect('Missing required prop').toHaveBeenWarned()
+    expect('Expected Number').toHaveBeenWarned()
+    expect(el.textContent).toBe('foo')
+  })
+
+  it('mixed syntax', function () {
+    new Vue({
+      el: el,
+      template: '<test :b="a" :c="d"></test>',
+      data: {
+        a: 'foo',
+        d: 'bar'
+      },
+      components: {
+        test: {
+          props: [
+            'b',
+            {
+              name: 'c',
+              type: Number
+            },
+            {
+              name: 'd',
+              required: true
+            }
+          ],
+          template: '<p>{{b}}</p><p>{{c}}</p>'
+        }
+      }
+    })
+    expect('Missing required prop').toHaveBeenWarned()
+    expect('Expected Number').toHaveBeenWarned()
+    expect(el.textContent).toBe('foo')
+  })
+
+  it('should respect default value of a Boolean prop', function () {
+    var vm = new Vue({
+      el: el,
+      template: '<test></test>',
+      components: {
+        test: {
+          props: {
+            prop: {
+              type: Boolean,
+              default: true
+            }
+          },
+          template: '{{prop}}'
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('true')
+  })
+
+  it('should initialize with default value when not provided & has default data', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<test></test>',
+      components: {
+        test: {
+          props: {
+            prop: {
+              type: String,
+              default: 'hello'
+            },
+            prop2: {
+              type: Object,
+              default: function () {
+                return { vm: this }
+              }
+            }
+          },
+          data: function () {
+            return {
+              other: 'world'
+            }
+          },
+          template: '{{prop}} {{other}}'
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('hello world')
+    // object/array default value initializers should be
+    // called with the correct `this` context
+    var child = vm.$children[0]
+    expect(child.prop2.vm).toBe(child)
+    vm.$children[0].prop = 'bye'
+    Vue.nextTick(function () {
+      expect(vm.$el.textContent).toBe('bye world')
+      done()
+    })
+  })
+
+  it('should warn data fields already defined as a prop', function () {
+    var Comp = Vue.extend({
+      data: function () {
+        return { a: 123 }
+      },
+      props: {
+        a: null
+      }
+    })
+    new Vue({
+      el: el,
+      template: '<comp a="1"></comp>',
+      components: {
+        comp: Comp
+      }
+    })
+    expect('already defined as a prop').toHaveBeenWarned()
+  })
+
+  it('propsData options', function () {
+    var vm = new Vue({
+      el: el,
+      props: {
+        a: null
+      },
+      propsData: {
+        a: 123
+      }
+    })
+    expect(getWarnCount()).toBe(0)
+    expect(vm.a).toBe(123)
+  })
+
+  // # GitHub issues #3183
+  it('pass propsData to create component that props is defined', function () {
+    var Comp = Vue.extend({
+      template: '<div>{{propA.a}}:{{propB.b}}</div>',
+      props: {
+        propA: {
+          type: Object,
+          required: true
+        },
+        'prop-b': {
+          type: Object,
+          required: true
+        }
+      }
+    })
+    var vm = new Comp({
+      el: el,
+      propsData: {
+        propA: { a: 123 },
+        propB: { b: '456' }
+      }
+    })
+    expect(vm.propA.a).toBe(123)
+    expect(vm.propB.b).toBe('456')
+    expect('Missing required prop: propA').not.toHaveBeenWarned()
+    expect('Invalid prop: type check failed for prop "propA". Expected Object, got Undefined').not.toHaveBeenWarned()
+    expect('Missing required prop: prop-b').not.toHaveBeenWarned()
+    expect('Invalid prop: type check failed for prop "prop-b". Expected Object, got Undefined').not.toHaveBeenWarned()
+  })
+
+  it('should warn using propsData during extension', function () {
+    Vue.extend({
+      propsData: {
+        a: 123
+      }
+    })
+    expect('propsData can only be used as an instantiation option').toHaveBeenWarned()
+  })
+
+  it('should not warn for non-required, absent prop', function () {
+    new Vue({
+      el: el,
+      template: '<test></test>',
+      components: {
+        test: {
+          props: {
+            prop: {
+              type: String
+            }
+          }
+        }
+      }
+    })
+    expect(getWarnCount()).toBe(0)
+  })
+
+  // #1683
+  it('should properly sync back up when mutating then replace', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: [1, 2]
+      },
+      template: '<comp :items.sync="items"></comp>',
+      components: {
+        comp: {
+          props: ['items']
+        }
+      }
+    })
+    var child = vm.$children[0]
+    child.items.push(3)
+    var newArray = child.items = [4]
+    Vue.nextTick(function () {
+      expect(child.items).toBe(newArray)
+      expect(vm.items).toBe(newArray)
+      done()
+    })
+  })
+
+  it('treat boolean props properly', function () {
+    var vm = new Vue({
+      el: el,
+      template: '<comp v-ref:child prop-a prop-b="prop-b"></comp>',
+      components: {
+        comp: {
+          props: {
+            propA: Boolean,
+            propB: Boolean,
+            propC: Boolean
+          }
+        }
+      }
+    })
+    expect(vm.$refs.child.propA).toBe(true)
+    expect(vm.$refs.child.propB).toBe(true)
+    expect(vm.$refs.child.propC).toBe(false)
+  })
+
+  it('detect possible camelCase prop usage', function () {
+    new Vue({
+      el: el,
+      template: '<comp propA="true" :propB="true" v-bind:propC.sync="true"></comp>',
+      components: {
+        comp: {
+          props: ['propA', 'propB', 'prop-c']
+        }
+      }
+    })
+    expect(getWarnCount()).toBe(3)
+    expect('did you mean `prop-a`').toHaveBeenWarned()
+    expect('did you mean `prop-b`').toHaveBeenWarned()
+    expect('did you mean `prop-c`').toHaveBeenWarned()
+  })
+
+  it('should use default for undefined values', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<comp :a="a"></comp>',
+      data: {
+        a: undefined
+      },
+      components: {
+        comp: {
+          template: '{{a}}',
+          props: {
+            a: {
+              default: 1
+            }
+          }
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('1')
+    vm.a = 2
+    Vue.nextTick(function () {
+      expect(vm.$el.textContent).toBe('2')
+      vm.a = undefined
+      Vue.nextTick(function () {
+        expect(vm.$el.textContent).toBe('1')
+        done()
+      })
+    })
+  })
+
+  it('non reactive values passed down as prop should not be converted', function (done) {
+    var a = Object.freeze({
+      nested: {
+        msg: 'hello'
+      }
+    })
+    var parent = new Vue({
+      el: el,
+      template: '<comp :a="a.nested"></comp>',
+      data: {
+        a: a
+      },
+      components: {
+        comp: {
+          props: ['a']
+        }
+      }
+    })
+    var child = parent.$children[0]
+    expect(child.a.msg).toBe('hello')
+    expect(child.a.__ob__).toBeUndefined() // should not be converted
+    parent.a = Object.freeze({
+      nested: {
+        msg: 'yo'
+      }
+    })
+    Vue.nextTick(function () {
+      expect(child.a.msg).toBe('yo')
+      expect(child.a.__ob__).toBeUndefined()
+      done()
+    })
+  })
+
+  it('inline prop values should be converted', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<comp :a="[1, 2, 3]"></comp>',
+      components: {
+        comp: {
+          props: ['a'],
+          template: '<div v-for="i in a">{{ i }}</div>'
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('123')
+    vm.$children[0].a.pop()
+    Vue.nextTick(function () {
+      expect(vm.$el.textContent).toBe('12')
+      done()
+    })
+  })
+
+  // #2549
+  it('mutating child prop binding should be reactive', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<comp :list="list"></comp>',
+      data: {
+        list: [1, 2, 3]
+      },
+      components: {
+        comp: {
+          props: ['list'],
+          template: '<div v-for="i in list">{{ i }}</div>',
+          created: function () {
+            this.list = [2, 3, 4]
+          }
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('234')
+    vm.$children[0].list.push(5)
+    Vue.nextTick(function () {
+      expect(vm.$el.textContent).toBe('2345')
+      done()
+    })
+  })
+
+  it('prop default value should be reactive', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<comp :list="list"></comp>',
+      data: {
+        list: undefined
+      },
+      components: {
+        comp: {
+          props: {
+            list: {
+              default: function () {
+                return [2, 3, 4]
+              }
+            }
+          },
+          template: '<div v-for="i in list">{{ i }}</div>'
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('234')
+    vm.$children[0].list.push(5)
+    Vue.nextTick(function () {
+      expect(vm.$el.textContent).toBe('2345')
+      done()
+    })
+  })
+
+  it('prop coerced value should be reactive', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<comp :obj="obj"></comp>',
+      data: {
+        obj: { ok: true }
+      },
+      components: {
+        comp: {
+          props: {
+            obj: {
+              coerce: function () {
+                return { ok: false }
+              }
+            }
+          },
+          template: '<div>{{ obj.ok }}</div>'
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('false')
+    vm.$children[0].obj.ok = true
+    Vue.nextTick(function () {
+      expect(vm.$el.textContent).toBe('true')
+      done()
+    })
+  })
+
+  it('prop coercion should be applied after defaulting', function () {
+    var vm = new Vue({
+      el: el,
+      template: '<comp></comp>',
+      components: {
+        comp: {
+          props: {
+            color: {
+              type: String,
+              default: 'blue',
+              coerce: function (color) {
+                return 'color-' + color
+              }
+            }
+          },
+          template: '<div>{{ color }}</div>'
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('color-blue')
+  })
+})
diff --git a/test/unit/specs/directives/internal/style_spec.js b/test/unit/specs/directives/internal/style_spec.js
new file mode 100644
index 00000000000..819d1a858b5
--- /dev/null
+++ b/test/unit/specs/directives/internal/style_spec.js
@@ -0,0 +1,130 @@
+var _ = require('src/util')
+var def = require('src/directives/internal/style')
+var Vue = require('src')
+
+function checkPrefixedProp (prop) {
+  var el = document.createElement('div')
+  var upper = prop.charAt(0).toUpperCase() + prop.slice(1)
+  if (!(prop in el.style)) {
+    var prefixes = ['Webkit', 'Moz', 'ms']
+    var i = prefixes.length
+    while (i--) {
+      if ((prefixes[i] + upper) in el.style) {
+        prop = prefixes[i] + upper
+      }
+    }
+  }
+  return prop
+}
+
+describe(':style', function () {
+  var el, dir
+  beforeEach(function () {
+    el = document.createElement('div')
+    dir = { el: el }
+    _.extend(dir, def)
+  })
+
+  it('plain CSS string', function () {
+    dir.update('color:red;')
+    expect(el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
+  })
+
+  it('!important', function () {
+    dir.update({
+      color: 'red !important;'
+    })
+    expect(el.style.getPropertyPriority('color')).toBe('important')
+  })
+
+  it('camel case', function () {
+    dir.update({
+      marginLeft: '30px'
+    })
+    expect(el.style.marginLeft).toBe('30px')
+  })
+
+  it('remove on falsy value', function () {
+    el.style.color = 'red'
+    dir.update({
+      color: null
+    })
+    expect(el.style.color).toBe('')
+  })
+
+  it('ignore unsupported property', function () {
+    dir.update({
+      unsupported: 'test'
+    })
+    expect(el.style.unsupported).not.toBe('test')
+  })
+
+  it('auto prefixing', function () {
+    var prop = checkPrefixedProp('transform')
+    var val = 'scale(0.5)'
+    dir.update({
+      transform: val
+    })
+    expect(el.style[prop]).toBe(val)
+  })
+
+  it('object with multiple fields', function () {
+    el.style.padding = '10px'
+
+    dir.update({
+      color: 'red',
+      marginRight: '30px'
+    })
+    expect(el.style.getPropertyValue('color')).toBe('red')
+    expect(el.style.getPropertyValue('margin-right')).toBe('30px')
+    expect(el.style.getPropertyValue('padding')).toBe('10px')
+
+    dir.update({
+      color: 'blue',
+      padding: null
+    })
+    expect(el.style.getPropertyValue('color')).toBe('blue')
+    expect(el.style.getPropertyValue('margin-right')).toBeFalsy()
+    expect(el.style.getPropertyValue('padding')).toBeFalsy()
+
+    // handle falsy value
+    dir.update(null)
+    expect(el.style.getPropertyValue('color')).toBeFalsy()
+    expect(el.style.getPropertyValue('margin-right')).toBeFalsy()
+    expect(el.style.getPropertyValue('padding')).toBeFalsy()
+  })
+
+  it('array of objects', function () {
+    el.style.padding = '10px'
+
+    dir.update([{color: 'red'}, {marginRight: '30px'}])
+    expect(el.style.getPropertyValue('color')).toBe('red')
+    expect(el.style.getPropertyValue('margin-right')).toBe('30px')
+    expect(el.style.getPropertyValue('padding')).toBe('10px')
+
+    dir.update([{color: 'blue'}, {padding: null}])
+    expect(el.style.getPropertyValue('color')).toBe('blue')
+    expect(el.style.getPropertyValue('margin-right')).toBeFalsy()
+    expect(el.style.getPropertyValue('padding')).toBeFalsy()
+  })
+
+  it('updates object deep', function (done) {
+    el.setAttribute(':style', 'divStyling')
+    var vm = new Vue({
+      el: el,
+      data: {divStyling: { display: 'none' }}
+    })
+    expect(el.style.display).toBe('none')
+    vm.divStyling.display = 'block'
+    _.nextTick(function () {
+      expect(el.style.display).toBe('block')
+      done()
+    })
+  })
+
+  // #2654
+  it('background size with only one value', function () {
+    dir.update({ backgroundSize: '100%' })
+    expect(el.style.cssText.replace(/\s/g, '')).toMatch(/background-size:100%(auto)?;/)
+  })
+})
diff --git a/test/unit/specs/directives/internal/transition_spec.js b/test/unit/specs/directives/internal/transition_spec.js
new file mode 100644
index 00000000000..7ba603213a8
--- /dev/null
+++ b/test/unit/specs/directives/internal/transition_spec.js
@@ -0,0 +1,110 @@
+var _ = require('src/util')
+var Vue = require('src')
+var Directive = require('src/directive')
+var def = require('src/directives/internal/transition')
+
+describe('transition', function () {
+  it('should instantiate a transition object with correct args', function () {
+    var fns = {}
+    var el = document.createElement('div')
+    var vm = new Vue({
+      transitions: {
+        test: fns
+      }
+    })
+    var dir = new Directive({
+      name: 'transition',
+      raw: '',
+      def: def,
+      modifiers: {
+        literal: true
+      }
+    }, vm, el)
+    dir._bind()
+    var transition = dir.el.__v_trans
+    expect(transition.enterClass).toBe('v-enter')
+    expect(transition.leaveClass).toBe('v-leave')
+    expect(dir.el.className).toBe('v-transition')
+    dir.update('test', '')
+    transition = dir.el.__v_trans
+    expect(transition.el).toBe(dir.el)
+    expect(transition.hooks).toBe(fns)
+    expect(transition.enterClass).toBe('test-enter')
+    expect(transition.leaveClass).toBe('test-leave')
+    expect(dir.el.className).toBe('test-transition')
+    dir.update('lol', 'test')
+    transition = dir.el.__v_trans
+    expect(transition.enterClass).toBe('lol-enter')
+    expect(transition.leaveClass).toBe('lol-leave')
+    expect(transition.fns).toBeUndefined()
+    expect(dir.el.className).toBe('lol-transition')
+  })
+
+  it('dynamic transitions', function (done) {
+    var el = document.createElement('div')
+    document.body.appendChild(el)
+    var calls = {
+      a: { enter: 0, leave: 0 },
+      b: { enter: 0, leave: 0 }
+    }
+    var vm = new Vue({
+      el: el,
+      template: '<div v-show="show" :transition="trans"></div>',
+      data: {
+        show: true,
+        trans: 'a'
+      },
+      transitions: {
+        a: {
+          enter: function (el, done) {
+            calls.a.enter++
+            done()
+          },
+          leave: function (el, done) {
+            calls.a.leave++
+            done()
+          }
+        },
+        b: {
+          enter: function (el, done) {
+            calls.b.enter++
+            done()
+          },
+          leave: function (el, done) {
+            calls.b.leave++
+            done()
+          }
+        }
+      }
+    })
+
+    assertCalls(0, 0, 0, 0)
+    vm.show = false
+    _.nextTick(function () {
+      assertCalls(0, 1, 0, 0)
+      vm.trans = 'b'
+      vm.show = true
+      _.nextTick(function () {
+        assertCalls(0, 1, 1, 0)
+        vm.show = false
+        _.nextTick(function () {
+          assertCalls(0, 1, 1, 1)
+          vm.trans = 'a'
+          vm.show = true
+          _.nextTick(function () {
+            assertCalls(1, 1, 1, 1)
+            done()
+          })
+        })
+      })
+    })
+
+    function assertCalls (a, b, c, d) {
+      expect(el.firstChild.style.display).toBe(vm.show ? '' : 'none')
+      expect(calls.a.enter).toBe(a)
+      expect(calls.a.leave).toBe(b)
+      expect(calls.b.enter).toBe(c)
+      expect(calls.b.leave).toBe(d)
+    }
+  })
+})
diff --git a/test/unit/specs/directives/public/bind_spec.js b/test/unit/specs/directives/public/bind_spec.js
new file mode 100644
index 00000000000..2f2d01c43b7
--- /dev/null
+++ b/test/unit/specs/directives/public/bind_spec.js
@@ -0,0 +1,93 @@
+var _ = require('src/util')
+var def = require('src/directives/public/bind')
+var xlinkNS = 'http://www.w3.org/1999/xlink'
+
+describe('v-bind', function () {
+  var el, dir
+  beforeEach(function () {
+    el = document.createElement('div')
+    dir = {
+      el: el,
+      descriptor: {},
+      modifiers: {}
+    }
+    _.extend(dir, def)
+  })
+
+  it('normal attr', function () {
+    dir.arg = 'test'
+    dir.update('ok')
+    expect(el.getAttribute('test')).toBe('ok')
+    dir.update('again')
+    expect(el.getAttribute('test')).toBe('again')
+    dir.update(null)
+    expect(el.hasAttribute('test')).toBe(false)
+    dir.update(false)
+    expect(el.hasAttribute('test')).toBe(false)
+    dir.update(true)
+    expect(el.getAttribute('test')).toBe('')
+    dir.update(0)
+    expect(el.getAttribute('test')).toBe('0')
+  })
+
+  it('should set property for input value', function () {
+    dir.el = document.createElement('input')
+    dir.arg = 'value'
+    dir.update('foo')
+    expect(dir.el.value).toBe('foo')
+    dir.el = document.createElement('input')
+    dir.el.type = 'checkbox'
+    dir.arg = 'checked'
+    expect(dir.el.checked).toBe(false)
+    dir.update(true)
+    expect(dir.el.checked).toBe(true)
+  })
+
+  it('xlink', function () {
+    dir.arg = 'xlink:special'
+    dir.update('ok')
+    expect(el.getAttributeNS(xlinkNS, 'special')).toBe('ok')
+    dir.update('again')
+    expect(el.getAttributeNS(xlinkNS, 'special')).toBe('again')
+    dir.update(null)
+    expect(el.hasAttributeNS(xlinkNS, 'special')).toBe(false)
+  })
+
+  it('object format', function () {
+    dir.el = document.createElement('input')
+    dir.update({
+      'test': 'foo',
+      'value': 'bar'
+    })
+    expect(dir.el.getAttribute('test')).toBe('foo')
+    expect(dir.el.value).toBe('bar')
+    dir.update({
+      'xlink:special': 'ok'
+    })
+    expect(dir.el.hasAttribute('test')).toBe(false)
+    expect(dir.el.value).toBeFalsy()
+    expect(dir.el.getAttributeNS(xlinkNS, 'special')).toBe('ok')
+    dir.update(null)
+    expect(dir.el.hasAttributeNS(xlinkNS, 'special')).toBe(false)
+  })
+
+  it('camel modifier', function () {
+    dir.modifiers.camel = true
+    var div = document.createElement('div')
+    div.innerHTML = '<svg></svg>'
+    dir.el = div.children[0]
+    dir.arg = 'view-box'
+    dir.update('0 0 1500 1000')
+    expect(dir.el.getAttribute('viewBox')).toBe('0 0 1500 1000')
+  })
+
+  it('enumrated non-boolean attributes', function () {
+    ['draggable', 'contenteditable', 'spellcheck'].forEach(function (attr) {
+      dir.arg = attr
+      dir.update(true)
+      expect(el.getAttribute(attr)).toBe('true')
+      dir.update(false)
+      expect(el.getAttribute(attr)).toBe('false')
+    })
+  })
+})
diff --git a/test/unit/specs/directives/public/cloak_spec.js b/test/unit/specs/directives/public/cloak_spec.js
new file mode 100644
index 00000000000..09925fa82c7
--- /dev/null
+++ b/test/unit/specs/directives/public/cloak_spec.js
@@ -0,0 +1,20 @@
+var compile = require('src/compiler').compile
+var Vue = require('src')
+
+describe('v-cloak', function () {
+  it('should not remove during compile', function () {
+    var el = document.createElement('div')
+    el.setAttribute('v-cloak', '')
+    compile(el, Vue.options)
+    expect(el.hasAttribute('v-cloak')).toBe(true)
+  })
+
+  it('should remove after compile', function () {
+    var el = document.createElement('div')
+    el.setAttribute('v-cloak', '')
+    new Vue({
+      el: el
+    })
+    expect(el.hasAttribute('v-cloak')).toBe(false)
+  })
+})
diff --git a/test/unit/specs/directives/public/el_spec.js b/test/unit/specs/directives/public/el_spec.js
new file mode 100644
index 00000000000..c7cdb98cf33
--- /dev/null
+++ b/test/unit/specs/directives/public/el_spec.js
@@ -0,0 +1,39 @@
+var _ = require('src/util')
+var Vue = require('src')
+
+describe('el', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+  })
+
+  it('normal', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        ok: true
+      },
+      template: '<div v-if="ok" v-el:test-el id="test"></div>'
+    })
+    expect(vm.$els.testEl).toBeTruthy()
+    expect(vm.$els.testEl.id).toBe('test')
+    vm.ok = false
+    _.nextTick(function () {
+      expect(vm.$els.testEl).toBeNull()
+      vm.ok = true
+      _.nextTick(function () {
+        expect(vm.$els.testEl.id).toBe('test')
+        done()
+      })
+    })
+  })
+
+  it('inside v-for', function () {
+    var vm = new Vue({
+      el: el,
+      data: { items: [1, 2] },
+      template: '<div v-for="n in items"><p v-el:test>{{n}}</p>{{$els.test.textContent}}</div>'
+    })
+    expect(vm.$el.textContent).toBe('1122')
+  })
+})
diff --git a/test/unit/specs/directives/public/for/for_ref_spec.js b/test/unit/specs/directives/public/for/for_ref_spec.js
new file mode 100644
index 00000000000..b0192e2b943
--- /dev/null
+++ b/test/unit/specs/directives/public/for/for_ref_spec.js
@@ -0,0 +1,79 @@
+var Vue = require('src')
+var _ = Vue.util
+
+describe('v-for + ref', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+  })
+
+  it('normal', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: { items: [1, 2, 3, 4, 5] },
+      template: '<test v-for="item in items" :item="item" v-ref:test></test>',
+      components: {
+        test: {
+          props: ['item']
+        }
+      }
+    })
+    expect(vm.$refs.test).toBeTruthy()
+    expect(Array.isArray(vm.$refs.test)).toBe(true)
+    expect(vm.$refs.test[0].item).toBe(1)
+    expect(vm.$refs.test[4].item).toBe(5)
+    vm.items = []
+    _.nextTick(function () {
+      expect(vm.$refs.test.length).toBe(0)
+      vm._directives[0].unbind()
+      expect(vm.$refs.test).toBeNull()
+      done()
+    })
+  })
+
+  it('object', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: {
+          a: 1,
+          b: 2
+        }
+      },
+      template: '<test v-for="item in items" :item="item" v-ref:test></test>',
+      components: {
+        test: {
+          props: ['item']
+        }
+      }
+    })
+    expect(vm.$refs.test).toBeTruthy()
+    expect(_.isPlainObject(vm.$refs.test)).toBe(true)
+    expect(vm.$refs.test.a.item).toBe(1)
+    expect(vm.$refs.test.b.item).toBe(2)
+    vm.items = { c: 3 }
+    _.nextTick(function () {
+      expect(Object.keys(vm.$refs.test).length).toBe(1)
+      expect(vm.$refs.test.c.item).toBe(3)
+      vm._directives[0].unbind()
+      expect(vm.$refs.test).toBeNull()
+      done()
+    })
+  })
+
+  it('nested', function () {
+    var vm = new Vue({
+      el: el,
+      template: '<c1 v-ref:c1></c1>',
+      components: {
+        c1: {
+          template: '<div v-for="n in 2" v-ref:c2></div>'
+        }
+      }
+    })
+    expect(vm.$refs.c1 instanceof Vue).toBe(true)
+    expect(vm.$refs.c2).toBeUndefined()
+    expect(Array.isArray(vm.$refs.c1.$refs.c2)).toBe(true)
+    expect(vm.$refs.c1.$refs.c2.length).toBe(2)
+  })
+})
diff --git a/test/unit/specs/directives/public/for/for_spec.js b/test/unit/specs/directives/public/for/for_spec.js
new file mode 100644
index 00000000000..379d09d209f
--- /dev/null
+++ b/test/unit/specs/directives/public/for/for_spec.js
@@ -0,0 +1,1337 @@
+var _ = require('src/util')
+var Vue = require('src')
+
+describe('v-for', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+  })
+
+  it('objects', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: [{a: 1}, {a: 2}]
+      },
+      template: '<div v-for="item in items">{{$index}} {{item.a}}</div>'
+    })
+    assertMutations(vm, el, done)
+  })
+
+  it('primitives', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: [1, 2, 3]
+      },
+      template: '<div v-for="item in items">{{$index}} {{item}}</div>'
+    })
+    assertPrimitiveMutations(vm, el, done)
+  })
+
+  it('object of objects', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: {
+          a: {a: 1},
+          b: {a: 2}
+        }
+      },
+      template: '<div v-for="item in items">{{$index}} {{$key}} {{item.a}}</div>'
+    })
+    assertObjectMutations(vm, el, done)
+  })
+
+  it('object of primitives', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: {
+          a: 1,
+          b: 2
+        }
+      },
+      template: '<div v-for="item in items">{{$index}} {{$key}} {{item}}</div>'
+    })
+    assertObjectPrimitiveMutations(vm, el, done)
+  })
+
+  it('array of arrays', function () {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: [[1, 1], [2, 2], [3, 3]]
+      },
+      template: '<div v-for="item in items">{{$index}} {{item}}</div>'
+    })
+    var markup = vm.items.map(function (item, i) {
+      return '<div>' + i + ' ' + item.toString() + '</div>'
+    }).join('')
+    expect(el.innerHTML).toBe(markup)
+  })
+
+  it('repeating object with filter', function () {
+    new Vue({
+      el: el,
+      data: {
+        items: {
+          a: { msg: 'aaa' },
+          b: { msg: 'bbb' }
+        }
+      },
+      template: '<div v-for="item in items | filterBy \'aaa\'">{{item.msg}}</div>'
+    })
+    expect(el.innerHTML).toBe('<div>aaa</div>')
+  })
+
+  it('filter converting array to object', function () {
+    new Vue({
+      el: el,
+      data: {
+        items: [
+          { msg: 'aaa' },
+          { msg: 'bbb' }
+        ]
+      },
+      template: '<div v-for="item in items | test">{{item.msg}} {{$key}}</div>',
+      filters: {
+        test: function (val) {
+          return {
+            a: val[0],
+            b: val[1]
+          }
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<div>aaa a</div><div>bbb b</div>')
+  })
+
+  it('check priorities: v-if before v-for', function () {
+    new Vue({
+      el: el,
+      data: {
+        items: [1, 2, 3]
+      },
+      template: '<div v-if="item < 3" v-for="item in items">{{item}}</div>'
+    })
+    expect(el.textContent).toBe('12')
+  })
+
+  it('check priorities: v-if after v-for', function () {
+    new Vue({
+      el: el,
+      data: {
+        items: [1, 2, 3]
+      },
+      template: '<div v-for="item in items" v-if="item < 3">{{item}}</div>'
+    })
+    expect(el.textContent).toBe('12')
+  })
+
+  it('component', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: [{a: 1}, {a: 2}]
+      },
+      template: '<test v-for="item in items" :index="$index" :item="item"></test>',
+      components: {
+        test: {
+          props: ['index', 'item'],
+          template: '<div>{{index}} {{item.a}}</div>',
+          replace: true
+        }
+      }
+    })
+    assertMutations(vm, el, done)
+  })
+
+  it('is component', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: [{a: 1}, {a: 2}]
+      },
+      template: '<p v-for="item in items" is="test" :index="$index" :item="item"></p>',
+      components: {
+        test: {
+          props: ['index', 'item'],
+          template: '<div>{{index}} {{item.a}}</div>',
+          replace: true
+        }
+      }
+    })
+    assertMutations(vm, el, done)
+  })
+
+  it('component with inline-template', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: [{a: 1}, {a: 2}]
+      },
+      template:
+        '<test v-for="item in items" :index="$index" :item="item" inline-template>' +
+          '{{index}} {{item.a}}' +
+        '</test>',
+      components: {
+        test: {
+          props: ['index', 'item']
+        }
+      }
+    })
+    assertMutations(vm, el, done)
+  })
+
+  it('component with primitive values', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: [1, 2, 3]
+      },
+      template: '<test v-for="item in items" :index="$index" :value="item"></test>',
+      components: {
+        test: {
+          props: ['index', 'value'],
+          template: '<div>{{index}} {{value}}</div>',
+          replace: true
+        }
+      }
+    })
+    assertPrimitiveMutations(vm, el, done)
+  })
+
+  it('component with object of objects', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: {
+          a: {a: 1},
+          b: {a: 2}
+        }
+      },
+      template: '<test v-for="item in items" :key="$key" :index="$index" :value="item"></test>',
+      components: {
+        test: {
+          props: ['key', 'index', 'value'],
+          template: '<div>{{index}} {{key}} {{value.a}}</div>',
+          replace: true
+        }
+      }
+    })
+    assertObjectMutations(vm, el, done)
+  })
+
+  it('nested loops', function () {
+    new Vue({
+      el: el,
+      data: {
+        items: [
+          { items: [{a: 1}, {a: 2}], a: 1 },
+          { items: [{a: 3}, {a: 4}], a: 2 }
+        ]
+      },
+      template: '<div v-for="item in items">' +
+          '<p v-for="subItem in item.items">{{$index}} {{subItem.a}} {{$parent.$index}} {{item.a}}</p>' +
+        '</div>'
+    })
+    expect(el.innerHTML).toBe(
+      '<div><p>0 1 0 1</p><p>1 2 0 1</p></div>' +
+      '<div><p>0 3 1 2</p><p>1 4 1 2</p></div>'
+    )
+  })
+
+  it('nested loops on object', function () {
+    new Vue({
+      el: el,
+      data: {
+        listHash: {
+          listA: [{a: 1}, {a: 2}],
+          listB: [{a: 1}, {a: 2}]
+        }
+      },
+      template:
+        '<div v-for="list in listHash">' +
+          '{{$key}}' +
+          '<p v-for="item in list">{{item.a}}</p>' +
+        '</div>'
+    })
+    function output (key) {
+      var key1 = key === 'listA' ? 'listB' : 'listA'
+      return '<div>' + key + '<p>1</p><p>2</p></div>' +
+             '<div>' + key1 + '<p>1</p><p>2</p></div>'
+    }
+    expect(el.innerHTML === output('listA') || el.innerHTML === output('listB')).toBeTruthy()
+  })
+
+  it('dynamic component type based on instance data', function () {
+    new Vue({
+      el: el,
+      template: '<component v-for="item in list" :is="\'view-\' + item.type"></component>',
+      data: {
+        list: [
+          { type: 'a' },
+          { type: 'b' },
+          { type: 'c' }
+        ]
+      },
+      components: {
+        'view-a': {
+          template: 'foo'
+        },
+        'view-b': {
+          template: 'bar'
+        },
+        'view-c': {
+          template: 'baz'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<component>foo</component><component>bar</component><component>baz</component>')
+    // primitive
+    el = document.createElement('div')
+    new Vue({
+      el: el,
+      template: '<component v-for="type in list" :is="\'view-\' + type"></component>',
+      data: {
+        list: ['a', 'b', 'c']
+      },
+      components: {
+        'view-a': {
+          template: 'foo'
+        },
+        'view-b': {
+          template: 'bar'
+        },
+        'view-c': {
+          template: 'baz'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<component>foo</component><component>bar</component><component>baz</component>')
+  })
+
+  it('fragment loop', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<template v-for="item in list"><p>{{item.a}}</p><p>{{item.a + 1}}</p></template>',
+      data: {
+        list: [
+          { a: 1 },
+          { a: 2 },
+          { a: 3 }
+        ]
+      }
+    })
+    assertMarkup()
+    vm.list.reverse()
+    _.nextTick(function () {
+      assertMarkup()
+      vm.list.splice(1, 1)
+      _.nextTick(function () {
+        assertMarkup()
+        vm.list.splice(1, 0, { a: 2 })
+        _.nextTick(function () {
+          assertMarkup()
+          done()
+        })
+      })
+    })
+
+    function assertMarkup () {
+      var markup = vm.list.map(function (item) {
+        return '<p>' + item.a + '</p><p>' + (item.a + 1) + '</p>'
+      }).join('')
+      expect(el.innerHTML).toBe(markup)
+    }
+  })
+
+  it('fragment loop with component', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<template v-for="item in list"><test :a="item.a"></test></template>',
+      data: {
+        list: [
+          { a: 1 },
+          { a: 2 },
+          { a: 3 }
+        ]
+      },
+      components: {
+        test: {
+          props: ['a'],
+          template: '{{a}}'
+        }
+      }
+    })
+    assertMarkup()
+    vm.list.reverse()
+    _.nextTick(function () {
+      assertMarkup()
+      vm.list.splice(1, 1)
+      _.nextTick(function () {
+        assertMarkup()
+        vm.list.splice(1, 0, { a: 2 })
+        _.nextTick(function () {
+          assertMarkup()
+          done()
+        })
+      })
+    })
+
+    function assertMarkup () {
+      var markup = vm.list.map(function (item) {
+        return '<test>' + item.a + '</test>'
+      }).join('')
+      expect(el.innerHTML).toBe(markup)
+    }
+  })
+
+  it('array filters', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="item in list | filterBy filterKey | orderBy sortKey -1 | limitBy 2">{{item.id}}</div>',
+      data: {
+        filterKey: 'foo',
+        sortKey: 'id',
+        list: [
+          { id: 1, id2: 4, msg: 'foo' },
+          { id: 2, id2: 3, msg: 'bar' },
+          { id: 3, id2: 2, msg: 'foo' },
+          { id: 4, id2: 1, msg: 'bar' }
+        ]
+      }
+    })
+    assertMarkup()
+
+    go(
+      function () {
+        vm.filterKey = 'bar'
+      }, assertMarkup
+    )
+    .then(
+      function () {
+        vm.sortKey = 'id2'
+      }, assertMarkup
+    )
+    .then(
+      function () {
+        vm.list[0].id2 = 0
+      }, assertMarkup
+    )
+    .then(
+      function () {
+        vm.list.push({ id: 0, id2: 4, msg: 'bar' })
+      }, assertMarkup
+    )
+    .then(
+      function () {
+        vm.list = [
+          { id: 33, id2: 4, msg: 'foo' },
+          { id: 44, id2: 3, msg: 'bar' }
+        ]
+      }, assertMarkup
+    )
+    .run(done)
+
+    function assertMarkup () {
+      var markup = vm.list
+        .filter(function (item) {
+          return item.msg === vm.filterKey
+        })
+        .sort(function (a, b) {
+          return a[vm.sortKey] > b[vm.sortKey] ? -1 : 1
+        })
+        .map(function (item) {
+          return '<div>' + item.id + '</div>'
+        })
+        .slice(0, 2)
+        .join('')
+      expect(el.innerHTML).toBe(markup)
+    }
+  })
+
+  it('orderBy supporting $key for object repeaters', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="val in obj | orderBy sortKey">{{val}}</div>',
+      data: {
+        sortKey: '$key',
+        obj: {
+          c: 1,
+          a: 3,
+          b: 2
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('<div>3</div><div>2</div><div>1</div>')
+    vm.sortKey = 'val'
+    _.nextTick(function () {
+      expect(el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
+      done()
+    })
+  })
+
+  it('orderBy supporting alias for primitive arrays', function () {
+    new Vue({
+      el: el,
+      template: '<div v-for="val in list | orderBy \'val\'">{{val}}</div>',
+      data: {
+        list: [3, 2, 1]
+      }
+    })
+    expect(el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
+  })
+
+  it('track by id', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<test v-for="item in list" :item="item" track-by="id"></test>',
+      data: {
+        list: [
+          { id: 1, msg: 'foo' },
+          { id: 2, msg: 'bar' },
+          { id: 3, msg: 'baz' }
+        ]
+      },
+      components: {
+        test: {
+          props: ['item'],
+          template: '{{item.msg}}'
+        }
+      }
+    })
+    assertMarkup()
+    var oldVms = vm.$children.slice()
+    // swap the data with different objects, but with
+    // the same ID!
+    vm.list = [
+      { id: 1, msg: 'qux' },
+      { id: 2, msg: 'quux' }
+    ]
+    _.nextTick(function () {
+      assertMarkup()
+      // should reuse old vms!
+      var i = 2
+      while (i--) {
+        expect(vm.$children[i]).toBe(oldVms[i])
+      }
+      done()
+    })
+
+    function assertMarkup () {
+      var markup = vm.list.map(function (item) {
+        return '<test>' + item.msg + '</test>'
+      }).join('')
+      expect(el.innerHTML).toBe(markup)
+    }
+  })
+
+  it('track by nested id path', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<test v-for="item in list" :item="item" track-by="nested.id"></test>',
+      data: {
+        list: [
+          { nested: { id: 1 }, msg: 'foo' },
+          { nested: { id: 2 }, msg: 'bar' },
+          { nested: { id: 3 }, msg: 'baz' }
+        ]
+      },
+      components: {
+        test: {
+          props: ['item'],
+          template: '{{item.msg}}'
+        }
+      }
+    })
+    assertMarkup()
+    var oldVms = vm.$children.slice()
+    // swap the data with different objects, but with
+    // the same ID!
+    vm.list = [
+      { nested: { id: 1 }, msg: 'qux' },
+      { nested: { id: 2 }, msg: 'quux' }
+    ]
+    _.nextTick(function () {
+      assertMarkup()
+      // should reuse old vms!
+      var i = 2
+      while (i--) {
+        expect(vm.$children[i]).toBe(oldVms[i])
+      }
+      done()
+    })
+
+    function assertMarkup () {
+      var markup = vm.list.map(function (item) {
+        return '<test>' + item.msg + '</test>'
+      }).join('')
+      expect(el.innerHTML).toBe(markup)
+    }
+  })
+
+  it('track by non-standard id path', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<test v-for="item in list" :item="item" track-by=".id"></test>',
+      data: {
+        list: [
+          { '.id': 1, msg: 'foo' },
+          { '.id': 2, msg: 'bar' },
+          { '.id': 3, msg: 'baz' }
+        ]
+      },
+      components: {
+        test: {
+          props: ['item'],
+          template: '{{item.msg}}'
+        }
+      }
+    })
+    assertMarkup()
+    var oldVms = vm.$children.slice()
+    // swap the data with different objects, but with
+    // the same ID!
+    vm.list = [
+      { '.id': 1, msg: 'qux' },
+      { '.id': 2, msg: 'quux' }
+    ]
+    _.nextTick(function () {
+      assertMarkup()
+      // should reuse old vms!
+      var i = 2
+      while (i--) {
+        expect(vm.$children[i]).toBe(oldVms[i])
+      }
+      done()
+    })
+
+    function assertMarkup () {
+      var markup = vm.list.map(function (item) {
+        return '<test>' + item.msg + '</test>'
+      }).join('')
+      expect(el.innerHTML).toBe(markup)
+    }
+  })
+
+  it('track by $index', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: [{a: 1}, {a: 2}]
+      },
+      template: '<div v-for="item in items" track-by="$index">{{$index}} {{item.a}}</div>'
+    })
+
+    assertMarkup()
+    var el1 = el.children[0]
+    var el2 = el.children[1]
+    vm.items = [{a: 3}, {a: 2}, {a: 1}]
+    _.nextTick(function () {
+      assertMarkup()
+      // should mutate the DOM in-place
+      expect(el.children[0]).toBe(el1)
+      expect(el.children[1]).toBe(el2)
+      done()
+    })
+
+    function assertMarkup () {
+      expect(el.innerHTML).toBe(vm.items.map(function (item, i) {
+        return '<div>' + i + ' ' + item.a + '</div>'
+      }).join(''))
+    }
+  })
+
+  it('primitive values track by $index', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        items: [1, 2, 3]
+      },
+      template: '<div v-for="item in items" track-by="$index">{{$index}} {{item}}</div>'
+    })
+    assertPrimitiveMutationsWithDuplicates(vm, el, done)
+  })
+
+  it('warn missing alias', function () {
+    new Vue({
+      el: el,
+      template: '<div v-for="items"></div>'
+    })
+    expect('alias is required').toHaveBeenWarned()
+  })
+
+  it('warn duplicate objects', function () {
+    var obj = {}
+    new Vue({
+      el: el,
+      template: '<div v-for="item in items"></div>',
+      data: {
+        items: [obj, obj]
+      }
+    })
+    expect('Duplicate value').toHaveBeenWarned()
+  })
+
+  it('warn duplicate objects on diff', function (done) {
+    var obj = {}
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="item in items"></div>',
+      data: {
+        items: [obj]
+      }
+    })
+    expect(getWarnCount()).toBe(0)
+    vm.items.push(obj)
+    _.nextTick(function () {
+      expect('Duplicate value').toHaveBeenWarned()
+      done()
+    })
+  })
+
+  it('warn duplicate trackby id', function () {
+    new Vue({
+      el: el,
+      template: '<div v-for="item in items" track-by="id"></div>',
+      data: {
+        items: [{id: 1}, {id: 1}]
+      }
+    })
+    expect('Duplicate value').toHaveBeenWarned()
+  })
+
+  it('key val syntax with object', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="(key,val) in items">{{$index}} {{key}} {{val.a}}</div>',
+      data: {
+        items: {
+          a: {a: 1},
+          b: {a: 2}
+        }
+      }
+    })
+    assertObjectMutations(vm, el, done)
+  })
+
+  it('key val syntax with array', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="(i, item) in items">{{i}} {{item.a}}</div>',
+      data: {
+        items: [{a: 1}, {a: 2}]
+      }
+    })
+    assertMutations(vm, el, done)
+  })
+
+  it('key val syntax with nested v-for s', function () {
+    new Vue({
+      el: el,
+      template: '<div v-for="(key,val) in items"><div v-for="(subkey,subval) in val">{{key}} {{subkey}} {{subval}}</div></div>',
+      data: {
+        items: {'a': {'b': 'c'}}
+      }
+    })
+    expect(el.innerHTML).toBe('<div><div>a b c</div></div>')
+  })
+
+  it('repeat number', function () {
+    new Vue({
+      el: el,
+      template: '<div v-for="n in 3">{{$index}} {{n}}</div>'
+    })
+    expect(el.innerHTML).toBe('<div>0 0</div><div>1 1</div><div>2 2</div>')
+  })
+
+  it('repeat string', function () {
+    new Vue({
+      el: el,
+      template: '<div v-for="letter in \'vue\'">{{$index}} {{letter}}</div>'
+    })
+    expect(el.innerHTML).toBe('<div>0 v</div><div>1 u</div><div>2 e</div>')
+  })
+
+  it('teardown', function () {
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="item in items"></div>',
+      data: {
+        items: [{a: 1}, {a: 2}]
+      }
+    })
+    vm._directives[0].unbind()
+    expect(vm.$children.length).toBe(0)
+  })
+
+  it('with transition', function (done) {
+    document.body.appendChild(el)
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="item in items" transition="test">{{item.a}}</div>',
+      data: {
+        items: [{a: 1}, {a: 2}, {a: 3}]
+      },
+      transitions: {
+        test: {
+          leave: function (el, done) {
+            setTimeout(done, 0)
+          }
+        }
+      }
+    })
+    vm.items.splice(1, 1, {a: 4})
+    setTimeout(function () {
+      expect(el.innerHTML).toBe(
+        '<div class="test-transition">1</div>' +
+        '<div class="test-transition">4</div>' +
+        '<div class="test-transition">3</div>'
+      )
+      document.body.removeChild(el)
+      done()
+    }, 100)
+  })
+
+  it('v-model binding on alias', function () {
+    var vm = new Vue({
+      el: el,
+      template:
+        '<div v-for="val in items"><input v-model="val"></div>' +
+        '<div v-for="val in obj"><input v-model="val"></div>',
+      data: {
+        items: ['a'],
+        obj: { foo: 'a' }
+      }
+    })
+
+    var a = getInput(1)
+    a.value = 'b'
+    trigger(a, 'input')
+    expect(vm.items[0]).toBe('b')
+
+    var b = getInput(2)
+    b.value = 'bar'
+    trigger(b, 'input')
+    expect(vm.obj.foo).toBe('bar')
+
+    function getInput (x) {
+      return vm.$el.querySelector('div:nth-child(' + x + ') input')
+    }
+  })
+
+  it('warn v-model on alias with filters', function () {
+    var vm = new Vue({
+      el: el,
+      template:
+        '<div v-for="item in items | orderBy \'item\'">' +
+          '<input v-model="item">' +
+        '</div>',
+      data: {
+        items: ['a', 'b']
+      }
+    })
+    trigger(vm.$el.querySelector('input'), 'input')
+    expect('It seems you are using two-way binding').toHaveBeenWarned()
+  })
+
+  it('nested track by', function (done) {
+    var vm = new Vue({
+      el: el,
+      template:
+        '<div v-for="item in list" track-by="id">' +
+          '{{item.msg}}' +
+          '<div v-for="subItem in item.list" track-by="id">' +
+            '{{subItem.msg}}' +
+          '</div>' +
+        '</div>',
+      data: {
+        list: [
+          { id: 1, msg: 'hi', list: [
+            { id: 1, msg: 'hi foo' }
+          ] },
+          { id: 2, msg: 'bar', list: [] },
+          { id: 3, msg: 'baz', list: [] }
+        ]
+      }
+    })
+    assertMarkup()
+
+    var oldNodes = el.children
+    var oldInnerNodes = el.children[0].children
+
+    vm.list = [
+      { id: 1, msg: 'baz', list: [
+        { id: 1, msg: 'hi foo' },
+        { id: 2, msg: 'hi bar' }
+      ] },
+      { id: 2, msg: 'qux', list: [] }
+    ]
+
+    _.nextTick(function () {
+      assertMarkup()
+      // should reuse old frags!
+      var i = 2
+      while (i--) {
+        expect(el.children[i]).toBe(oldNodes[i])
+      }
+      expect(el.children[0].children[0]).toBe(oldInnerNodes[0])
+      done()
+    })
+
+    function assertMarkup () {
+      var markup = vm.list.map(function (item) {
+        var sublist = item.list.map(function (item) {
+          return '<div>' + item.msg + '</div>'
+        }).join('')
+        return '<div>' + item.msg + sublist + '</div>'
+      }).join('')
+      expect(el.innerHTML).toBe(markup)
+    }
+  })
+
+  it('switch between object-converted & array mode', function (done) {
+    var obj = {
+      a: { msg: 'foo' },
+      b: { msg: 'bar' }
+    }
+    var arr = [obj.b, obj.a]
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="item in obj">{{item.msg}}</div>',
+      data: {
+        obj: obj
+      }
+    })
+    expect(el.innerHTML).toBe(Object.keys(obj).map(function (key) {
+      return '<div>' + obj[key].msg + '</div>'
+    }).join(''))
+    vm.obj = arr
+    _.nextTick(function () {
+      expect(el.innerHTML).toBe('<div>bar</div><div>foo</div>')
+      // make sure it cleared the cache
+      expect(vm._directives[0].cache.a).toBeNull()
+      expect(vm._directives[0].cache.b).toBeNull()
+      done()
+    })
+  })
+
+  it('call attach/detach for contained components', function (done) {
+    document.body.appendChild(el)
+    var attachSpy = jasmine.createSpy('attach')
+    var detachSpy = jasmine.createSpy('detach')
+    var vm = new Vue({
+      el: el,
+      template: '<test v-for="item in items"></test>',
+      data: {
+        items: [1, 2]
+      },
+      components: {
+        test: {
+          attached: attachSpy,
+          detached: detachSpy
+        }
+      }
+    })
+    expect(attachSpy.calls.count()).toBe(2)
+    expect(detachSpy.calls.count()).toBe(0)
+    vm.items.push(3)
+    _.nextTick(function () {
+      expect(attachSpy.calls.count()).toBe(3)
+      expect(detachSpy.calls.count()).toBe(0)
+      vm.items.pop()
+      _.nextTick(function () {
+        expect(attachSpy.calls.count()).toBe(3)
+        expect(detachSpy.calls.count()).toBe(1)
+        vm.items = []
+        _.nextTick(function () {
+          expect(attachSpy.calls.count()).toBe(3)
+          expect(detachSpy.calls.count()).toBe(3)
+          done()
+        })
+      })
+    })
+  })
+
+  it('access parent\'s $refs', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<c1 v-ref:c1><div v-for="n in 2">{{$refs.c1.d}}</div></c1>',
+      components: {
+        c1: {
+          template: '<div><slot></slot></div>',
+          data: function () {
+            return {
+              d: 1
+            }
+          }
+        }
+      }
+    })
+    expect(vm.$refs.c1 instanceof Vue).toBe(true)
+    expect(vm.$refs.c1.$el.innerHTML).toContain('<div>1</div><div>1</div>')
+  })
+
+  it('access parent scope\'s $els', function (done) {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<div data-d=1 v-el:a><div v-for="n in 2">{{ready ? $els.a.getAttribute("data-d") : 0}}</div></div>',
+      data: {
+        ready: false
+      }
+    })
+    expect(vm.$els.a.nodeType).toBe(1)
+    expect(vm.$els.a.innerHTML).toContain('<div>0</div><div>0</div>')
+    vm.ready = true
+    vm.$nextTick(function () {
+      expect(vm.$els.a.innerHTML).toContain('<div>1</div><div>1</div>')
+      done()
+    })
+  })
+
+  it('warning for frozen objects', function () {
+    new Vue({
+      el: document.createElement('div'),
+      template: '<div v-for="item in items">{{item.name}}</div>',
+      data: {
+        items: [Object.freeze({ name: 'hi' })]
+      }
+    })
+    expect('Frozen v-for objects cannot be automatically tracked').toHaveBeenWarned()
+  })
+
+  it('warn v-if and v-for mixed usage', function () {
+    new Vue({
+      el: document.createElement('div'),
+      template: '<div v-for="item in items" v-if="ok"></div>',
+      data: {
+        items: []
+      }
+    })
+    expect('consider filtering the source Array instead').toHaveBeenWarned()
+  })
+})
+
+/**
+ * Simple helper for chained async asssertions
+ *
+ * @param {Function} fn - the data manipulation function
+ * @param {Function} cb - the assertion fn to be called on nextTick
+ */
+
+function go (fn, cb) {
+  return {
+    stack: [{fn: fn, cb: cb}],
+    then: function (fn, cb) {
+      this.stack.push({fn: fn, cb: cb})
+      return this
+    },
+    run: function (done) {
+      var self = this
+      var step = this.stack.shift()
+      if (!step) return done()
+      step.fn()
+      _.nextTick(function () {
+        step.cb()
+        self.run(done)
+      })
+    }
+  }
+}
+
+/**
+ * Assert mutation and markup correctness for v-for on
+ * an Array of Objects
+ */
+
+function assertMutations (vm, el, done) {
+  assertMarkup()
+  var poppedItem
+  go(
+    function () {
+      vm.items.push({a: 3})
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.push(vm.items.shift())
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.reverse()
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      poppedItem = vm.items.pop()
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.unshift(poppedItem)
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.sort(function (a, b) {
+        return a.a > b.a ? 1 : -1
+      })
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.splice(1, 1, {a: 5})
+    },
+    assertMarkup
+  )
+  // test swapping the array
+  .then(
+    function () {
+      vm.items = [{a: 0}, {a: 1}, {a: 2}]
+    },
+    assertMarkup
+  )
+  .run(done)
+
+  function assertMarkup () {
+    var tag = el.children[0].tagName.toLowerCase()
+    var markup = vm.items.map(function (item, i) {
+      var el = '<' + tag + '>' + i + ' ' + item.a + '</' + tag + '>'
+      return el
+    }).join('')
+    expect(el.innerHTML).toBe(markup)
+  }
+}
+
+/**
+ * Assert mutation and markup correctness for v-for on
+ * an Array of primitive values
+ */
+
+function assertPrimitiveMutations (vm, el, done) {
+  assertMarkup()
+  go(
+    function () {
+      vm.items.push(4)
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.shift()
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.reverse()
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.pop()
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.unshift(1)
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.sort(function (a, b) {
+        return a > b ? 1 : -1
+      })
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.splice(1, 1, 5)
+    },
+    assertMarkup
+  )
+  // test swapping the array
+  .then(
+    function () {
+      vm.items = [1, 2, 3]
+    },
+    assertMarkup
+  )
+  .run(done)
+
+  function assertMarkup () {
+    var markup = vm.items.map(function (item, i) {
+      return '<div>' + i + ' ' + item + '</div>'
+    }).join('')
+    expect(el.innerHTML).toBe(markup)
+  }
+}
+
+/**
+ * Assert mutation and markup correctness for v-for on
+ * an Array of primitive values when using track-by="$index"
+ */
+
+function assertPrimitiveMutationsWithDuplicates (vm, el, done) {
+  assertMarkup()
+  go(
+    function () {
+      vm.items.push(2, 2, 3)
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.shift()
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.reverse()
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.pop()
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.unshift(3)
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.sort(function (a, b) {
+        return a > b ? 1 : -1
+      })
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items.splice(1, 1, 5)
+    },
+    assertMarkup
+  )
+  // test swapping the array
+  .then(
+    function () {
+      vm.items = [1, 2, 2]
+    },
+    assertMarkup
+  )
+  .run(done)
+
+  function assertMarkup () {
+    var markup = vm.items.map(function (item, i) {
+      return '<div>' + i + ' ' + item + '</div>'
+    }).join('')
+    expect(el.innerHTML).toBe(markup)
+  }
+}
+
+/**
+ * Assert mutation and markup correctness for v-for on
+ * an Object of Objects
+ */
+
+function assertObjectMutations (vm, el, done) {
+  assertMarkup()
+  go(
+    function () {
+      vm.items.a = {a: 3}
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items = {
+        c: {a: 1},
+        d: {a: 2}
+      }
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      _.set(vm.items, 'a', {a: 3})
+    },
+    assertMarkup
+  )
+  .run(done)
+
+  function assertMarkup () {
+    var markup = Object.keys(vm.items).map(function (key, i) {
+      return '<div>' + i + ' ' + key + ' ' + vm.items[key].a + '</div>'
+    }).join('')
+    expect(el.innerHTML).toBe(markup)
+  }
+}
+
+/**
+ * Assert mutation and markup correctness for v-for on
+ * an Object of primitive values
+ */
+
+function assertObjectPrimitiveMutations (vm, el, done) {
+  assertMarkup()
+  go(
+    function () {
+      vm.items.a = 3
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      vm.items = {
+        c: 1,
+        d: 2
+      }
+    },
+    assertMarkup
+  )
+  .then(
+    function () {
+      _.set(vm.items, 'a', 3)
+    },
+    assertMarkup
+  )
+  .run(done)
+
+  function assertMarkup () {
+    var markup = Object.keys(vm.items).map(function (key, i) {
+      return '<div>' + i + ' ' + key + ' ' + vm.items[key] + '</div>'
+    }).join('')
+    expect(el.innerHTML).toBe(markup)
+  }
+}
+
+/**
+ * Helper for triggering events
+ */
+
+function trigger (target, event, process) {
+  var e = document.createEvent('HTMLEvents')
+  e.initEvent(event, true, true)
+  if (process) process(e)
+  target.dispatchEvent(e)
+  return e
+}
diff --git a/test/unit/specs/directives/public/for/for_stagger_spec.js b/test/unit/specs/directives/public/for/for_stagger_spec.js
new file mode 100644
index 00000000000..38126683876
--- /dev/null
+++ b/test/unit/specs/directives/public/for/for_stagger_spec.js
@@ -0,0 +1,165 @@
+var Vue = require('src')
+var _ = Vue.util
+
+describe('v-for staggering transitions', function () {
+  var el
+  var delayAmount = 50
+  var multiplier = 2.5 // the bigger the slower, but safer
+  beforeEach(function () {
+    el = document.createElement('div')
+    document.body.appendChild(el)
+  })
+
+  afterEach(function () {
+    document.body.removeChild(el)
+  })
+
+  it('as attribute', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="item in list" transition="stagger" stagger="' + delayAmount + '">{{item.a}}</div>',
+      data: {
+        list: []
+      },
+      transitions: {
+        stagger: {
+          enter: function (el, done) {
+            _.nextTick(done)
+          },
+          leave: function (el, done) {
+            _.nextTick(done)
+          }
+        }
+      }
+    })
+    assertStagger(vm, done)
+  })
+
+  it('as hook', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="item in list" transition="stagger">{{item.a}}</div>',
+      data: {
+        list: []
+      },
+      transitions: {
+        stagger: {
+          stagger: function (i) {
+            return i * delayAmount
+          },
+          enter: function (el, done) {
+            _.nextTick(done)
+          },
+          leave: function (el, done) {
+            _.nextTick(done)
+          }
+        }
+      }
+    })
+    assertStagger(vm, done)
+  })
+
+  it('remove while staggered', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="item in list" transition="stagger" stagger="' + delayAmount + '">{{item.a}}</div>',
+      data: {
+        list: []
+      },
+      transitions: {
+        stagger: {
+          enter: function (el, done) {
+            _.nextTick(done)
+          },
+          leave: function (el, done) {
+            _.nextTick(done)
+          }
+        }
+      }
+    })
+    vm.list = [{a: 1}, {a: 2}]
+    expect(el.innerHTML).toBe('')
+    _.nextTick(function () {
+      expect(el.children.length).toBe(1)
+      expect(el.children[0].className).toBe('stagger-transition stagger-enter')
+      expect(el.children[0].textContent).toBe('1')
+      vm.list = [vm.list[0]] // remove second
+      setTimeout(function () {
+        // should have only one
+        expect(el.innerHTML).toBe('<div class="stagger-transition">1</div>')
+        done()
+      }, delayAmount * multiplier)
+    })
+  })
+
+  it('reorder while staggered', function (done) {
+    var vm = new Vue({
+      el: el,
+      template: '<div v-for="item in list" transition="stagger" stagger="' + delayAmount + '">{{item.a}}</div>',
+      data: {
+        list: []
+      },
+      transitions: {
+        stagger: {
+          enter: function (el, done) {
+            _.nextTick(done)
+          },
+          leave: function (el, done) {
+            _.nextTick(done)
+          }
+        }
+      }
+    })
+    vm.list = [{a: 1}, {a: 2}, {a: 3}]
+    expect(el.innerHTML).toBe('')
+    _.nextTick(function () {
+      expect(el.children.length).toBe(1)
+      expect(el.children[0].className).toBe('stagger-transition stagger-enter')
+      expect(el.children[0].textContent).toBe('1')
+      vm.list = [vm.list[2], vm.list[1], vm.list[0]] // reorder
+      setTimeout(function () {
+        // should have correct order
+        expect(el.innerHTML).toBe(
+          '<div class="stagger-transition">3</div>' +
+          '<div class="stagger-transition">2</div>' +
+          '<div class="stagger-transition">1</div>'
+        )
+        done()
+      }, delayAmount * 3)
+    })
+  })
+
+  function assertStagger (vm, done) {
+    vm.list = [{a: 1}, {a: 2}]
+    expect(el.innerHTML).toBe('')
+    _.nextTick(function () {
+      expect(el.children.length).toBe(1)
+      expect(el.children[0].className).toBe('stagger-transition stagger-enter')
+      expect(el.children[0].textContent).toBe('1')
+      _.nextTick(function () {
+        expect(el.innerHTML).toBe('<div class="stagger-transition">1</div>')
+        setTimeout(function () {
+          expect(el.innerHTML).toBe(
+            '<div class="stagger-transition">1</div>' +
+            '<div class="stagger-transition">2</div>'
+          )
+          vm.list = []
+          _.nextTick(function () {
+            expect(el.children.length).toBe(2)
+            expect(el.children[0].className).toBe('stagger-transition stagger-leave')
+            expect(el.children[0].textContent).toBe('1')
+            expect(el.children[1].className).toBe('stagger-transition')
+            expect(el.children[1].textContent).toBe('2')
+            _.nextTick(function () {
+              expect(el.innerHTML).toBe('<div class="stagger-transition">2</div>')
+              setTimeout(function () {
+                expect(el.innerHTML).toBe('')
+                done()
+              }, delayAmount * multiplier)
+            })
+          })
+        }, delayAmount * multiplier)
+      })
+    })
+  }
+})
diff --git a/test/unit/specs/directives/public/html_spec.js b/test/unit/specs/directives/public/html_spec.js
new file mode 100644
index 00000000000..ebb22f75c44
--- /dev/null
+++ b/test/unit/specs/directives/public/html_spec.js
@@ -0,0 +1,51 @@
+var _ = require('src/util')
+var def = require('src/directives/public/html')
+
+describe('v-html', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+  })
+
+  it('element', function () {
+    var dir = {
+      el: el
+    }
+    _.extend(dir, def)
+    dir.bind()
+    dir.update('<div>1234</div><p>234</p>')
+    expect(el.innerHTML).toBe('<div>1234</div><p>234</p>')
+    dir.update('<p>123</p><div>444</div>')
+    expect(el.innerHTML).toBe('<p>123</p><div>444</div>')
+    dir.update(null)
+    expect(el.innerHTML).toBe('')
+  })
+
+  it('inline', function () {
+    var node = document.createComment('html-test')
+    el.appendChild(node)
+    var dir = {
+      el: node
+    }
+    _.extend(dir, def)
+    dir.bind()
+    dir.update('<div>1234</div><p>234</p>')
+    expect(el.innerHTML).toBe('<div>1234</div><p>234</p>')
+    dir.update('<p>123</p><div>444</div>')
+    expect(el.innerHTML).toBe('<p>123</p><div>444</div>')
+    dir.update(null)
+    expect(el.innerHTML).toBe('')
+  })
+
+  it('inline keep whitespace', function () {
+    var node = document.createComment('html-test')
+    el.appendChild(node)
+    var dir = {
+      el: node
+    }
+    _.extend(dir, def)
+    dir.bind()
+    dir.update('    <p>span</p>    ')
+    expect(el.innerHTML).toBe('    <p>span</p>    ')
+  })
+})
diff --git a/test/unit/specs/directives/public/if_spec.js b/test/unit/specs/directives/public/if_spec.js
new file mode 100644
index 00000000000..f9d28fc9f72
--- /dev/null
+++ b/test/unit/specs/directives/public/if_spec.js
@@ -0,0 +1,435 @@
+var Vue = require('src')
+var nextTick = Vue.nextTick
+
+describe('v-if', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+  })
+
+  it('normal', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: { test: false, a: 'A' },
+      template: '<div v-if="test"><test :a="a"></test></div>',
+      components: {
+        test: {
+          props: ['a'],
+          template: '{{a}}'
+        }
+      }
+    })
+    // lazy instantitation
+    expect(el.innerHTML).toBe('')
+    expect(vm.$children.length).toBe(0)
+    vm.test = true
+    nextTick(function () {
+      expect(el.innerHTML).toBe('<div><test>A</test></div>')
+      expect(vm.$children.length).toBe(1)
+      vm.test = false
+      nextTick(function () {
+        expect(el.innerHTML).toBe('')
+        expect(vm.$children.length).toBe(0)
+        vm.test = true
+        nextTick(function () {
+          expect(el.innerHTML).toBe('<div><test>A</test></div>')
+          expect(vm.$children.length).toBe(1)
+          var child = vm.$children[0]
+          vm.$destroy()
+          expect(child._isDestroyed).toBe(true)
+          done()
+        })
+      })
+    })
+  })
+
+  it('template block', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: { test: false, a: 'A', b: 'B' },
+      template: '<template v-if="test"><p>{{a}}</p><p>{{b}}</p></template>'
+    })
+    // lazy instantitation
+    expect(el.innerHTML).toBe('')
+    vm.test = true
+    nextTick(function () {
+      expect(el.innerHTML).toBe('<p>A</p><p>B</p>')
+      vm.test = false
+      nextTick(function () {
+        expect(el.innerHTML).toBe('')
+        done()
+      })
+    })
+  })
+
+  it('v-if + component', function (done) {
+    var attachSpy = jasmine.createSpy()
+    var detachSpy = jasmine.createSpy()
+    var readySpy = jasmine.createSpy()
+    var vm = new Vue({
+      el: el,
+      data: { ok: false },
+      template: '<test v-if="ok"></test>',
+      components: {
+        test: {
+          data: function () {
+            return { a: 123 }
+          },
+          template: '{{a}}',
+          ready: readySpy,
+          attached: attachSpy,
+          detached: detachSpy
+        }
+      }
+    })
+    vm.$appendTo(document.body)
+    expect(el.innerHTML).toBe('')
+    expect(vm.$children.length).toBe(0)
+    vm.ok = true
+    nextTick(function () {
+      expect(el.innerHTML).toBe('<test>123</test>')
+      expect(vm.$children.length).toBe(1)
+      expect(attachSpy).toHaveBeenCalled()
+      expect(readySpy).toHaveBeenCalled()
+      vm.ok = false
+      nextTick(function () {
+        expect(detachSpy).toHaveBeenCalled()
+        expect(el.innerHTML).toBe('')
+        expect(vm.$children.length).toBe(0)
+        vm.$remove()
+        done()
+      })
+    })
+  })
+
+  it('v-if + dynamic component', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        ok: false,
+        view: 'view-a'
+      },
+      template: '<component :is="view" v-if="ok"></component>',
+      components: {
+        'view-a': {
+          template: 'foo'
+        },
+        'view-b': {
+          template: 'bar'
+        }
+      }
+    })
+    expect(el.innerHTML).toBe('')
+    expect(vm.$children.length).toBe(0)
+    // toggle if with lazy instantiation
+    vm.ok = true
+    nextTick(function () {
+      expect(el.innerHTML).toBe('<component>foo</component>')
+      expect(vm.$children.length).toBe(1)
+      // switch view when if=true
+      vm.view = 'view-b'
+      nextTick(function () {
+        expect(el.innerHTML).toBe('<component>bar</component>')
+        expect(vm.$children.length).toBe(1)
+        // toggle if when already instantiated
+        vm.ok = false
+        nextTick(function () {
+          expect(el.innerHTML).toBe('')
+          expect(vm.$children.length).toBe(0)
+          // toggle if and switch view at the same time
+          vm.view = 'view-a'
+          vm.ok = true
+          nextTick(function () {
+            expect(el.innerHTML).toBe('<component>foo</component>')
+            expect(vm.$children.length).toBe(1)
+            done()
+          })
+        })
+      })
+    })
+  })
+
+  it('v-if with different truthy values', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        a: 1
+      },
+      template: '<div v-if="a">{{a}}</div>'
+    })
+    expect(el.innerHTML).toBe('<div>1</div>')
+    vm.a = 2
+    nextTick(function () {
+      expect(el.innerHTML).toBe('<div>2</div>')
+      done()
+    })
+  })
+
+  it('invalid warn', function () {
+    el.setAttribute('v-if', 'test')
+    new Vue({
+      el: el
+    })
+    expect('cannot be used on an instance root element').toHaveBeenWarned()
+  })
+
+  it('call attach/detach for transcluded components', function (done) {
+    document.body.appendChild(el)
+    var attachSpy = jasmine.createSpy('attached')
+    var detachSpy = jasmine.createSpy('detached')
+    var vm = new Vue({
+      el: el,
+      data: { show: true },
+      template: '<outer><transcluded></transcluded></outer>',
+      components: {
+        outer: {
+          template: '<div v-if="$parent.show"><slot></slot></div>'
+        },
+        transcluded: {
+          template: 'transcluded',
+          attached: attachSpy,
+          detached: detachSpy
+        }
+      }
+    })
+    expect(attachSpy).toHaveBeenCalled()
+    vm.show = false
+    nextTick(function () {
+      expect(detachSpy).toHaveBeenCalled()
+      document.body.removeChild(el)
+      done()
+    })
+  })
+
+  it('call attach/detach for dynamicly created components inside if block', function (done) {
+    document.body.appendChild(el)
+    var attachSpy = jasmine.createSpy('attached')
+    var detachSpy = jasmine.createSpy('detached')
+    var transcluded = {
+      props: ['a'],
+      template: '{{a}}',
+      attached: attachSpy,
+      detached: detachSpy
+    }
+    var vm = new Vue({
+      el: el,
+      data: {
+        show: true,
+        list: [{a: 0}]
+      },
+      template:
+        '<outer>' +
+          '<div>' + // an extra layer to test components deep inside the tree
+            '<transcluded v-for="item in list" :a="item.a"></transcluded>' +
+          '</div>' +
+        '</outer>',
+      components: {
+        outer: {
+          template:
+            '<div v-if="$parent.show">' +
+              '<slot></slot>' +
+            '</div>' +
+            // this is to test that compnents that are not in the if block
+            // should not fire attach/detach when v-if toggles
+            '<transcluded></transcluded>',
+          components: {
+            transcluded: transcluded
+          }
+        },
+        transcluded: transcluded
+      }
+    })
+    assertMarkup()
+    expect(attachSpy.calls.count()).toBe(2)
+    vm.show = false
+    nextTick(function () {
+      assertMarkup()
+      expect(detachSpy.calls.count()).toBe(1)
+      vm.list.push({a: 1})
+      vm.show = true
+      nextTick(function () {
+        assertMarkup()
+        expect(attachSpy.calls.count()).toBe(2 + 2)
+        vm.list.push({a: 2})
+        vm.show = false
+        nextTick(function () {
+          assertMarkup()
+          expect(attachSpy.calls.count()).toBe(2 + 2 + 1)
+          expect(detachSpy.calls.count()).toBe(1 + 3)
+          document.body.removeChild(el)
+          done()
+        })
+      })
+    })
+
+    function assertMarkup () {
+      var showBlock = vm.show
+        ? '<div><div>' +
+            vm.list.map(function (o) {
+              return '<transcluded>' + o.a + '</transcluded>'
+            }).join('') +
+          '</div></div>'
+        : ''
+      var markup =
+        '<outer>' +
+            showBlock +
+          '<transcluded></transcluded>' +
+        '</outer>'
+      expect(el.innerHTML).toBe(markup)
+    }
+  })
+
+  it('call attach/detach for nested ifs', function (done) {
+    var attachSpy = jasmine.createSpy('attached')
+    var detachSpy = jasmine.createSpy('detached')
+    document.body.appendChild(el)
+    var vm = new Vue({
+      el: el,
+      data: {
+        showOuter: true,
+        showInner: false
+      },
+      template:
+        '<div v-if="showOuter">' +
+          '<div v-if="showInner">' +
+            '<test></test>' +
+          '</div>' +
+        '</div>',
+      components: {
+        test: {
+          attached: attachSpy,
+          detached: detachSpy
+        }
+      }
+    })
+    expect(attachSpy).not.toHaveBeenCalled()
+    vm.showInner = true
+    nextTick(function () {
+      expect(attachSpy.calls.count()).toBe(1)
+      vm.showOuter = false
+      nextTick(function () {
+        expect(detachSpy.calls.count()).toBe(1)
+        document.body.removeChild(el)
+        done()
+      })
+    })
+  })
+
+  // #893 in IE textNodes do not have `contains` method
+  it('call attach/detach: comparing textNodes in IE', function (done) {
+    document.body.appendChild(el)
+    var attachSpy = jasmine.createSpy('attached')
+    var detachSpy = jasmine.createSpy('detached')
+    var vm = new Vue({
+      el: el,
+      data: {
+        show: true
+      },
+      template: '<template v-if="show"><test></test></template>',
+      components: {
+        test: {
+          template: 'foo',
+          replace: true,
+          attached: attachSpy,
+          detached: detachSpy
+        }
+      }
+    })
+    assertMarkup()
+    assertCalls(1, 0)
+    vm.show = false
+    nextTick(function () {
+      assertMarkup()
+      assertCalls(1, 1)
+      vm.show = true
+      nextTick(function () {
+        assertMarkup()
+        assertCalls(2, 1)
+        vm.show = false
+        nextTick(function () {
+          assertMarkup()
+          assertCalls(2, 2)
+          document.body.removeChild(el)
+          done()
+        })
+      })
+    })
+
+    function assertMarkup () {
+      expect(el.innerHTML).toBe(vm.show ? 'foo' : '')
+    }
+
+    function assertCalls (attach, detach) {
+      expect(attachSpy.calls.count()).toBe(attach)
+      expect(detachSpy.calls.count()).toBe(detach)
+    }
+  })
+
+  // #1097 v-if components not having correct parent
+  it('compile with correct transclusion host', function () {
+    var parentA
+    var parentB
+    new Vue({
+      el: el,
+      data: {
+        show: true
+      },
+      template: '<parent><child v-if="show"></child></parent>',
+      components: {
+        parent: {
+          template: '<slot></slot>',
+          created: function () {
+            parentA = this
+          }
+        },
+        child: {
+          created: function () {
+            parentB = this.$parent
+          }
+        }
+      }
+    })
+    expect(parentA).toBeTruthy()
+    expect(parentA).toBe(parentB)
+  })
+
+  it('if + else', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: { test: false, a: 'A', b: 'B' },
+      template: '<div v-if="test">{{a}}</div><div v-else>{{b}}</div>'
+    })
+    expect(el.textContent).toBe('B')
+    vm.test = true
+    nextTick(function () {
+      expect(el.textContent).toBe('A')
+      vm.test = false
+      nextTick(function () {
+        expect(el.textContent).toBe('B')
+        done()
+      })
+    })
+  })
+
+  it('else block teardown', function (done) {
+    var created = jasmine.createSpy()
+    var destroyed = jasmine.createSpy()
+    var vm = new Vue({
+      el: el,
+      data: { ok: false },
+      template: '<div v-if="ok"></div><div v-else><test></test></div>',
+      components: {
+        test: {
+          created: created,
+          destroyed: destroyed
+        }
+      }
+    })
+    expect(created.calls.count()).toBe(1)
+    vm.$destroy()
+    nextTick(function () {
+      expect(destroyed.calls.count()).toBe(1)
+      done()
+    })
+  })
+})
diff --git a/test/unit/specs/directives/public/model_spec.js b/test/unit/specs/directives/public/model_spec.js
new file mode 100644
index 00000000000..fbc58c4bc41
--- /dev/null
+++ b/test/unit/specs/directives/public/model_spec.js
@@ -0,0 +1,791 @@
+var _ = require('src/util')
+var Vue = require('src')
+
+// unset jQuery to bypass jQuery check for normal test cases
+jQuery = null
+
+/**
+ * Mock event helper
+ */
+
+function trigger (target, event, process) {
+  var e = document.createEvent('HTMLEvents')
+  e.initEvent(event, true, true)
+  if (process) process(e)
+  target.dispatchEvent(e)
+}
+
+/**
+ * setting <select>'s value in IE9 doesn't work
+ * we have to manually loop through the options
+ */
+
+function updateSelect (el, value) {
+  var options = el.options
+  var i = options.length
+  while (i--) {
+    /* eslint-disable eqeqeq */
+    if (options[i].value == value) {
+    /* eslint-enable eqeqeq */
+      options[i].selected = true
+      break
+    }
+  }
+}
+
+describe('v-model', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+    el.style.display = 'none'
+    document.body.appendChild(el)
+  })
+
+  it('radio buttons', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: '1'
+      },
+      template:
+        '<input type="radio" value="1" v-model="test" name="test" number>' +
+        '<input type="radio" value="2" v-model="test" name="test">'
+    })
+    expect(el.childNodes[0].checked).toBe(true)
+    expect(el.childNodes[1].checked).toBe(false)
+    vm.test = '2'
+    _.nextTick(function () {
+      expect(el.childNodes[0].checked).toBe(false)
+      expect(el.childNodes[1].checked).toBe(true)
+      el.childNodes[0].click()
+      expect(el.childNodes[0].checked).toBe(true)
+      expect(el.childNodes[1].checked).toBe(false)
+      expect(vm.test).toBe(1)
+      vm._directives[1]._teardown()
+      el.childNodes[1].click()
+      expect(vm.test).toBe(1)
+      done()
+    })
+  })
+
+  it('radio default value', function () {
+    var vm = new Vue({
+      el: el,
+      data: {},
+      template: '<input type="radio" checked value="a" v-model="test">'
+    })
+    expect(vm.test).toBe('a')
+  })
+
+  it('radio expression', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: false,
+        test2: 'string1',
+        expression1: 'string1',
+        expression2: 'string2'
+      },
+      template:
+        '<input type="radio" value="1" v-model="test" name="test" :value="true">' +
+        '<input type="radio" value="0" v-model="test" name="test" :value="false">' +
+        '<input type="radio" value="1" v-model="test2" name="test2" :value="expression1">' +
+        '<input type="radio" value="0" v-model="test2" name="test2" :value="expression2">'
+    })
+    expect(el.childNodes[0].checked).toBe(false)
+    expect(el.childNodes[1].checked).toBe(true)
+    expect(el.childNodes[2].checked).toBe(true)
+    expect(el.childNodes[3].checked).toBe(false)
+    _.nextTick(function () {
+      el.childNodes[0].click()
+      expect(vm.test).toBe(true)
+      el.childNodes[3].click()
+      expect(vm.test2).toBe('string2')
+      done()
+    })
+  })
+
+  it('checkbox', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: true
+      },
+      template: '<input type="checkbox" v-model="test">'
+    })
+    expect(el.firstChild.checked).toBe(true)
+    vm.test = false
+    _.nextTick(function () {
+      expect(el.firstChild.checked).toBe(false)
+      expect(vm.test).toBe(false)
+      el.firstChild.click()
+      expect(el.firstChild.checked).toBe(true)
+      expect(vm.test).toBe(true)
+      vm._directives[0]._teardown()
+      el.firstChild.click()
+      expect(el.firstChild.checked).toBe(false)
+      expect(vm.test).toBe(true)
+      done()
+    })
+  })
+
+  it('checkbox default value', function () {
+    var vm = new Vue({
+      el: el,
+      data: {},
+      template: '<input type="checkbox" checked v-model="test">'
+    })
+    expect(vm.test).toBe(true)
+  })
+
+  it('checkbox expression', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: '',
+        expression1: 'aTrueValue',
+        expression2: 'aFalseValue'
+      },
+      template: '<input type="checkbox" v-model="test" :true-value="expression1" :false-value="expression2">'
+    })
+    expect(vm.test).toBe('')
+    el.firstChild.click()
+    expect(vm.test).toBe('aTrueValue')
+    expect(el.firstChild.checked).toBe(true)
+    el.firstChild.click()
+    expect(vm.test).toBe('aFalseValue')
+    expect(el.firstChild.checked).toBe(false)
+    _.nextTick(function () {
+      vm.test = 'aTrueValue'
+      _.nextTick(function () {
+        expect(el.firstChild.checked).toBe(true)
+        done()
+      })
+    })
+  })
+
+  it('checkbox + array model', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        list: [1],
+        a: {}
+      },
+      template:
+        '<input type="checkbox" v-model="list" number value="1">' +
+        '<input type="checkbox" v-model="list" :value="a">'
+    })
+    expect(el.firstChild.checked).toBe(true)
+    expect(el.lastChild.checked).toBe(false)
+    el.firstChild.click()
+    expect(vm.list.length).toBe(0)
+    el.lastChild.click()
+    expect(vm.list.length).toBe(1)
+    expect(vm.list[0]).toBe(vm.a)
+    el.firstChild.click()
+    expect(vm.list.length).toBe(2)
+    expect(vm.list[1]).toBe(1)
+    _.nextTick(function () {
+      vm.list = [vm.a]
+      _.nextTick(function () {
+        expect(el.firstChild.checked).toBe(false)
+        expect(el.lastChild.checked).toBe(true)
+        done()
+      })
+    })
+  })
+
+  it('checkbox + array model default value', function () {
+    var vm = new Vue({
+      el: el,
+      data: {
+        list: [],
+        a: {}
+      },
+      template:
+        '<input type="checkbox" v-model="list" number value="1">' +
+        '<input type="checkbox" checked v-model="list" :value="a">'
+    })
+    expect(vm.list.length).toBe(1)
+    expect(vm.list[0]).toBe(vm.a)
+  })
+
+  it('select', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'b'
+      },
+      template:
+        '<select v-model="test">' +
+          '<option>a</option>' +
+          '<option>b</option>' +
+          '<option>c</option>' +
+        '</select>'
+    })
+    expect(vm.test).toBe('b')
+    expect(el.firstChild.value).toBe('b')
+    expect(el.firstChild.childNodes[1].selected).toBe(true)
+    vm.test = 'c'
+    _.nextTick(function () {
+      expect(el.firstChild.value).toBe('c')
+      expect(el.firstChild.childNodes[2].selected).toBe(true)
+      updateSelect(el.firstChild, 'a')
+      trigger(el.firstChild, 'change')
+      expect(vm.test).toBe('a')
+      done()
+    })
+  })
+
+  it('select persist non-selected on append', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: null
+      },
+      replace: true,
+      template:
+        '<select v-model="test">' +
+          '<option>a</option>' +
+          '<option>b</option>' +
+          '<option>c</option>' +
+        '</select>'
+    })
+    _.nextTick(function () {
+      expect(vm.$el.value).toBe('')
+      expect(vm.$el.selectedIndex).toBe(-1)
+      vm.$remove()
+      vm.$appendTo(document.body)
+      _.nextTick(function () {
+        expect(vm.$el.value).toBe('')
+        expect(vm.$el.selectedIndex).toBe(-1)
+        done()
+      })
+    })
+  })
+
+  it('select template default value', function () {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'a'
+      },
+      template:
+        '<select v-model="test">' +
+          '<option>a</option>' +
+          '<option selected>b</option>' +
+        '</select>'
+    })
+    expect(vm.test).toBe('b')
+    expect(el.firstChild.value).toBe('b')
+    expect(el.firstChild.childNodes[1].selected).toBe(true)
+  })
+
+  it('select + empty default value', function () {
+    var vm = new Vue({
+      el: el,
+      template: '<select v-model="test"><option value="" selected>null</option><<option value="1">1</option></select>'
+    })
+    expect(vm.test).toBe('')
+    trigger(vm.$el.firstChild, 'change')
+    expect(vm.test).toBe('')
+  })
+
+  it('select + multiple', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: [2] // test number soft equal
+      },
+      template:
+        '<select v-model="test" multiple>' +
+          '<option>1</option>' +
+          '<option>2</option>' +
+          '<option>3</option>' +
+        '</select>'
+    })
+    var opts = el.firstChild.options
+    expect(opts[0].selected).toBe(false)
+    expect(opts[1].selected).toBe(true)
+    expect(opts[2].selected).toBe(false)
+    vm.test = [1, '3'] // mix of number/string
+    _.nextTick(function () {
+      expect(opts[0].selected).toBe(true)
+      expect(opts[1].selected).toBe(false)
+      expect(opts[2].selected).toBe(true)
+      opts[0].selected = false
+      opts[1].selected = true
+      trigger(el.firstChild, 'change')
+      expect(vm.test[0]).toBe('2')
+      expect(vm.test[1]).toBe('3')
+      done()
+    })
+  })
+
+  it('select + multiple default value', function () {
+    var vm = new Vue({
+      el: el,
+      data: {},
+      template:
+        '<select v-model="test" multiple>' +
+          '<option>a</option>' +
+          '<option selected>b</option>' +
+          '<option selected>c</option>' +
+        '</select>'
+    })
+    expect(vm.test[0]).toBe('b')
+    expect(vm.test[1]).toBe('c')
+  })
+
+  it('select + number', function () {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: '1'
+      },
+      template: '<select v-model="test" number><option value="1">1</option></select>'
+    })
+    expect(vm.test).toBe('1')
+    trigger(vm.$el.firstChild, 'change')
+    expect(vm.test).toBe(1)
+  })
+
+  it('select + number + multiple', function () {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: []
+      },
+      template: '<select v-model="test" multiple number><option>1</option><option>2</option></select>'
+    })
+    ;[].forEach.call(el.querySelectorAll('option'), function (o) {
+      o.selected = true
+    })
+    trigger(el.firstChild, 'change')
+    expect(vm.test[0]).toBe(1)
+    expect(vm.test[1]).toBe(2)
+  })
+
+  it('select + number initial value', function () {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: '1'
+      },
+      template: '<select v-model="test" number><option value="1" selected>1</option></select>'
+    })
+    expect(vm.test).toBe(1)
+  })
+
+  it('select + v-for', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: { msg: 'A' },
+        opts: [
+          { text: 'a', value: { msg: 'A' }},
+          { text: 'b', value: { msg: 'B' }}
+        ]
+      },
+      template:
+        '<select v-model="test">' +
+          '<option v-for="op in opts" :value="op.value">{{op.text}}</option>' +
+        '</select>'
+    })
+    var select = el.firstChild
+    var opts = select.options
+    expect(opts[0].selected).toBe(true)
+    expect(opts[1].selected).toBe(false)
+    expect(vm.test.msg).toBe('A')
+    opts[1].selected = true
+    trigger(select, 'change')
+    _.nextTick(function () {
+      expect(opts[0].selected).toBe(false)
+      expect(opts[1].selected).toBe(true)
+      expect(vm.test.msg).toBe('B')
+      vm.test = { msg: 'A' }
+      _.nextTick(function () {
+        expect(opts[0].selected).toBe(true)
+        expect(opts[1].selected).toBe(false)
+        vm.test = { msg: 'C' }
+        vm.opts.push({text: 'c', value: vm.test})
+        _.nextTick(function () {
+          // updating the opts array should force the
+          // v-model to update
+          expect(opts[0].selected).toBe(false)
+          expect(opts[1].selected).toBe(false)
+          expect(opts[2].selected).toBe(true)
+          done()
+        })
+      })
+    })
+  })
+
+  it('text', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'b'
+      },
+      template: '<input v-model="test">'
+    })
+    expect(el.firstChild.value).toBe('b')
+    vm.test = 'a'
+    _.nextTick(function () {
+      expect(el.firstChild.value).toBe('a')
+      el.firstChild.value = 'c'
+      trigger(el.firstChild, 'input')
+      expect(vm.test).toBe('c')
+      vm._directives[0]._teardown()
+      el.firstChild.value = 'd'
+      trigger(el.firstChild, 'input')
+      expect(vm.test).toBe('c')
+      done()
+    })
+  })
+
+  it('text default value', function () {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'b'
+      },
+      template: '<input v-model="test | test" value="a">',
+      filters: {
+        test: {
+          read: function (v) {
+            return v.slice(0, -1)
+          },
+          write: function (v) {
+            return v + 'c'
+          }
+        }
+      }
+    })
+    expect(vm.test).toBe('ac')
+    expect(el.firstChild.value).toBe('a')
+  })
+
+  it('text lazy', function () {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'b'
+      },
+      template: '<input v-model="test" lazy>'
+    })
+    expect(el.firstChild.value).toBe('b')
+    expect(vm.test).toBe('b')
+    el.firstChild.value = 'c'
+    trigger(el.firstChild, 'input')
+    expect(vm.test).toBe('b')
+    trigger(el.firstChild, 'change')
+    expect(vm.test).toBe('c')
+  })
+
+  it('text with filters', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'b'
+      },
+      filters: {
+        test: {
+          write: function (val) {
+            return val.toLowerCase()
+          }
+        }
+      },
+      template: '<input v-model="test | uppercase | test">'
+    })
+    expect(el.firstChild.value).toBe('B')
+    trigger(el.firstChild, 'focus')
+    el.firstChild.value = 'cc'
+    trigger(el.firstChild, 'input')
+    _.nextTick(function () {
+      expect(el.firstChild.value).toBe('cc')
+      expect(vm.test).toBe('cc')
+      trigger(el.firstChild, 'change')
+      trigger(el.firstChild, 'blur')
+      _.nextTick(function () {
+        expect(el.firstChild.value).toBe('CC')
+        expect(vm.test).toBe('cc')
+        done()
+      })
+    })
+  })
+
+  it('text with only write filter', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'b'
+      },
+      filters: {
+        test: {
+          write: function (val) {
+            return val.toUpperCase()
+          }
+        }
+      },
+      template: '<input v-model="test | test">'
+    })
+    trigger(el.firstChild, 'focus')
+    el.firstChild.value = 'cc'
+    trigger(el.firstChild, 'input')
+    _.nextTick(function () {
+      expect(el.firstChild.value).toBe('cc')
+      expect(vm.test).toBe('CC')
+      trigger(el.firstChild, 'change')
+      trigger(el.firstChild, 'blur')
+      _.nextTick(function () {
+        expect(el.firstChild.value).toBe('CC')
+        expect(vm.test).toBe('CC')
+        done()
+      })
+    })
+  })
+
+  it('number', function () {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 1
+      },
+      template: '<input v-model="test" value="2" number>'
+    })
+    expect(vm.test).toBe(2)
+    el.firstChild.value = 3
+    trigger(el.firstChild, 'input')
+    expect(vm.test).toBe(3)
+  })
+
+  it('IE9 cut and delete', function (done) {
+    var ie9 = _.isIE9
+    _.isIE9 = true
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'foo'
+      },
+      template: '<input v-model="test">'
+    })
+    var input = el.firstChild
+    input.value = 'bar'
+    trigger(input, 'cut')
+    _.nextTick(function () {
+      expect(vm.test).toBe('bar')
+      input.value = 'a'
+      trigger(input, 'keyup', function (e) {
+        e.keyCode = 8
+      })
+      expect(vm.test).toBe('a')
+      // teardown
+      vm._directives[0]._teardown()
+      input.value = 'bar'
+      trigger(input, 'keyup', function (e) {
+        e.keyCode = 8
+      })
+      expect(vm.test).toBe('a')
+      _.isIE9 = ie9
+      done()
+    })
+  })
+
+  if (!_.isAndroid) {
+    it('text + compositionevents', function (done) {
+      var vm = new Vue({
+        el: el,
+        data: {
+          test: 'foo',
+          test2: 'bar'
+        },
+        template: '<input v-model="test"><input v-model="test2 | uppercase">'
+      })
+      var input = el.firstChild
+      var input2 = el.childNodes[1]
+      trigger(input, 'compositionstart')
+      trigger(input2, 'compositionstart')
+      input.value = input2.value = 'baz'
+      // input before composition unlock should not call set
+      trigger(input, 'input')
+      trigger(input2, 'input')
+      expect(vm.test).toBe('foo')
+      expect(vm.test2).toBe('bar')
+      // after composition unlock it should work
+      trigger(input, 'compositionend')
+      trigger(input2, 'compositionend')
+      trigger(input, 'input')
+      trigger(input2, 'input')
+      expect(vm.test).toBe('baz')
+      expect(vm.test2).toBe('baz')
+      // IE complains about "unspecified error" when calling
+      // setSelectionRange() on an input element that's been
+      // removed from the DOM, so we wait until the
+      // selection range callback has fired to end this test.
+      _.nextTick(done)
+    })
+  }
+
+  it('textarea', function () {
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'foo',
+        b: 'bar'
+      },
+      template: '<textarea v-model="test">foo {{b}} baz</textarea>'
+    })
+    expect(vm.test).toBe('foo bar baz')
+    expect(el.firstChild.value).toBe('foo bar baz')
+  })
+
+  it('warn invalid tag', function () {
+    new Vue({
+      el: el,
+      template: '<div v-model="test"></div>'
+    })
+    expect('does not support element type').toHaveBeenWarned()
+  })
+
+  it('warn read-only filters', function () {
+    new Vue({
+      el: el,
+      template: '<input v-model="abc | test">',
+      filters: {
+        test: function (v) {
+          return v
+        }
+      }
+    })
+    expect('read-only filter').toHaveBeenWarned()
+  })
+
+  it('support jQuery change event', function (done) {
+    // restore jQuery
+    jQuery = $
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'b'
+      },
+      template: '<input v-model="test">'
+    })
+    expect(el.firstChild.value).toBe('b')
+    vm.test = 'a'
+    _.nextTick(function () {
+      expect(el.firstChild.value).toBe('a')
+      el.firstChild.value = 'c'
+      jQuery(el.firstChild).trigger('change')
+      expect(vm.test).toBe('c')
+      vm._directives[0]._teardown()
+      el.firstChild.value = 'd'
+      jQuery(el.firstChild).trigger('change')
+      expect(vm.test).toBe('c')
+      // unset jQuery
+      jQuery = null
+      done()
+    })
+  })
+
+  it('support debounce', function (done) {
+    var spy = jasmine.createSpy()
+    var vm = new Vue({
+      el: el,
+      data: {
+        test: 'a'
+      },
+      watch: {
+        test: spy
+      },
+      template: '<input v-model="test" debounce="100">'
+    })
+    el.firstChild.value = 'b'
+    trigger(el.firstChild, 'input')
+    setTimeout(function () {
+      el.firstChild.value = 'c'
+      trigger(el.firstChild, 'input')
+    }, 10)
+    setTimeout(function () {
+      el.firstChild.value = 'd'
+      trigger(el.firstChild, 'input')
+    }, 20)
+    setTimeout(function () {
+      expect(spy.calls.count()).toBe(0)
+      expect(vm.test).toBe('a')
+    }, 30)
+    setTimeout(function () {
+      expect(spy.calls.count()).toBe(1)
+      expect(spy).toHaveBeenCalledWith('d', 'a')
+      expect(vm.test).toBe('d')
+      setTimeout(function () {
+        el.firstChild.value = 'e'
+        // change should trigger change instantly without debounce
+        trigger(el.firstChild, 'change')
+        trigger(el.firstChild, 'blur')
+        _.nextTick(function () {
+          expect(spy.calls.count()).toBe(2)
+          expect(spy).toHaveBeenCalledWith('e', 'd')
+          expect(vm.test).toBe('e')
+          done()
+        })
+      }, 10)
+    }, 200)
+  })
+
+  it('update on bind value change', function (done) {
+    var vm = new Vue({
+      el: el,
+      template:
+        '<input type="radio" v-model="a" checked :value="b">' +
+        '<input type="radio" v-model="a" :value="c">',
+      data: {
+        a: 0,
+        b: 1,
+        c: 2
+      }
+    })
+    // should sync inline-checked value to a
+    expect(vm.a).toBe(1)
+    vm.b = 3
+    _.nextTick(function () {
+      expect(vm.a).toBe(3)
+      expect(el.firstChild.checked).toBe(true)
+      expect(el.lastChild.checked).toBe(false)
+      vm.a = 2
+      _.nextTick(function () {
+        expect(el.firstChild.checked).toBe(false)
+        expect(el.lastChild.checked).toBe(true)
+        done()
+      })
+    })
+  })
+
+  it('should not sync value on blur when parent fragment is removed', function (done) {
+    el.style.display = ''
+    var vm = new Vue({
+      el: el,
+      replace: false,
+      template:
+        '<form v-if="ok" @submit.prevent="save">' +
+          '<input v-model="msg">' +
+        '</form>',
+      data: {
+        ok: true,
+        msg: 'foo'
+      },
+      methods: {
+        save: function () {
+          this.ok = false
+          this.msg = ''
+        }
+      }
+    })
+    el.querySelector('input').focus()
+    trigger(el.querySelector('form'), 'submit')
+    _.nextTick(function () {
+      expect(vm.msg).toBe('')
+      done()
+    })
+  })
+})
diff --git a/test/unit/specs/directives/public/on_spec.js b/test/unit/specs/directives/public/on_spec.js
new file mode 100644
index 00000000000..eda11e03b88
--- /dev/null
+++ b/test/unit/specs/directives/public/on_spec.js
@@ -0,0 +1,675 @@
+var _ = require('src/util')
+var Vue = require('src')
+
+function trigger (target, event, process) {
+  var e = document.createEvent('HTMLEvents')
+  e.initEvent(event, true, true)
+  if (process) process(e)
+  target.dispatchEvent(e)
+  return e
+}
+
+describe('v-on', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+  })
+
+  it('methods', function () {
+    var spy = jasmine.createSpy()
+    var vm = new Vue({
+      el: el,
+      template: '<a v-on:click="test"></a>',
+      data: {a: 1},
+      methods: {
+        test: spy
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'click')
+    expect(spy.calls.count()).toBe(1)
+    vm.$destroy()
+    trigger(a, 'click')
+    expect(spy.calls.count()).toBe(1)
+  })
+
+  it('shorthand', function () {
+    var spy = jasmine.createSpy()
+    var vm = new Vue({
+      el: el,
+      template: '<a @click="test"></a>',
+      data: {a: 1},
+      methods: {
+        test: spy
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'click')
+    expect(spy.calls.count()).toBe(1)
+    vm.$destroy()
+    trigger(a, 'click')
+    expect(spy.calls.count()).toBe(1)
+  })
+
+  it('inline expression', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:click="a++">{{a}}</a>',
+      data: {a: 1}
+    })
+    var a = el.firstChild
+    trigger(a, 'click')
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with key modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.enter="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 13
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with ctrl modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.ctrl="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.ctrlKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with shift modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.shift="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.shiftKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with ctrl + shift modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.ctrl.shift="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.ctrlKey = true
+      e.shiftKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with ctrl + alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.ctrl.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.ctrlKey = true
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with shift + alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.shift.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.shiftKey = true
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with ctrl + shift + alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.ctrl.shift.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.ctrlKey = true
+      e.shiftKey = true
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + shift modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.shift="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.shiftKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + ctrl modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.ctrl="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.ctrlKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + shift + alt modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.shift.alt="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.shiftKey = true
+      e.altKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + shift + ctrl modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.shift.ctrl="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.shiftKey = true
+      e.ctrlKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + alt + ctrl modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.alt.ctrl="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.altKey = true
+      e.ctrlKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with meta + shift + alt + ctrl modifier', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a.meta.shift.alt.ctrl="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+      e.metaKey = true
+      e.shiftKey = true
+      e.altKey = true
+      e.ctrlKey = true
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with delete modifier capturing DEL', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.delete="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 46
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with delete modifier capturing backspace', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.delete="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 8
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with key modifier (keycode)', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.13="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 13
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('with key modifier (letter)', function (done) {
+    new Vue({
+      el: el,
+      template: '<a v-on:keyup.a="test">{{a}}</a>',
+      data: {a: 1},
+      methods: {
+        test: function () {
+          this.a++
+        }
+      }
+    })
+    var a = el.firstChild
+    trigger(a, 'keyup', function (e) {
+      e.keyCode = 65
+    })
+    _.nextTick(function () {
+      expect(a.textContent).toBe('2')
+      done()
+    })
+  })
+
+  it('stop modifier', function () {
+    var outer = jasmine.createSpy('outer')
+    var inner = jasmine.createSpy('inner')
+    new Vue({
+      el: el,
+      template: '<div @click="outer"><div class="inner" @click.stop="inner"></div></div>',
+      methods: {
+        outer: outer,
+        inner: inner
+      }
+    })
+    trigger(el.querySelector('.inner'), 'click')
+    expect(inner).toHaveBeenCalled()
+    expect(outer).not.toHaveBeenCalled()
+  })
+
+  it('prevent modifier', function () {
+    var prevented
+    new Vue({
+      el: el,
+      template: '<a href="#" @click.prevent="onClick">',
+      methods: {
+        onClick: function (e) {
+          // store the prevented state now:
+          // IE will reset the `defaultPrevented` flag
+          // once the event handler call stack is done!
+          prevented = e.defaultPrevented
+        }
+      }
+    })
+    trigger(el.firstChild, 'click')
+    expect(prevented).toBe(true)
+  })
+
+  it('prevent modifier with no value', function () {
+    new Vue({
+      el: el,
+      template: '<a href="#123" @click.prevent>'
+    })
+    var hash = window.location.hash
+    trigger(el.firstChild, 'click')
+    expect(window.location.hash).toBe(hash)
+  })
+
+  it('capture modifier', function () {
+    document.body.appendChild(el)
+    var outer = jasmine.createSpy('outer')
+    var inner = jasmine.createSpy('inner')
+    new Vue({
+      el: el,
+      template: '<div @click.capture.stop="outer"><div class="inner" @click="inner"></div></div>',
+      methods: {
+        outer: outer,
+        inner: inner
+      }
+    })
+    trigger(el.querySelector('.inner'), 'click')
+    expect(outer).toHaveBeenCalled()
+    expect(inner).not.toHaveBeenCalled()
+    document.body.removeChild(el)
+  })
+
+  it('self modifier', function () {
+    var outer = jasmine.createSpy('outer')
+    new Vue({
+      el: el,
+      template: '<div class="outer" @click.self="outer"><div class="inner"></div></div>',
+      methods: {
+        outer: outer
+      }
+    })
+    trigger(el.querySelector('.inner'), 'click')
+    expect(outer).not.toHaveBeenCalled()
+    trigger(el.querySelector('.outer'), 'click')
+    expect(outer).toHaveBeenCalled()
+  })
+
+  it('multiple modifiers working together', function () {
+    var outer = jasmine.createSpy('outer')
+    var prevented
+    new Vue({
+      el: el,
+      template: '<div @keyup="outer"><input class="inner" @keyup.enter.stop.prevent="inner"></div></div>',
+      methods: {
+        outer: outer,
+        inner: function (e) {
+          prevented = e.defaultPrevented
+        }
+      }
+    })
+    trigger(el.querySelector('.inner'), 'keyup', function (e) {
+      e.keyCode = 13
+    })
+    expect(outer).not.toHaveBeenCalled()
+    expect(prevented).toBe(true)
+  })
+
+  it('warn non-function values', function () {
+    new Vue({
+      el: el,
+      data: { test: 123 },
+      template: '<a v-on:keyup="test"></a>'
+    })
+    expect('expects a function value').toHaveBeenWarned()
+  })
+
+  it('iframe', function () {
+    // iframes only gets contentWindow when inserted
+    // into the document
+    document.body.appendChild(el)
+    var spy = jasmine.createSpy()
+    var vm = new Vue({
+      el: el,
+      template: '<iframe v-on:click="test"></iframe>',
+      methods: {
+        test: spy
+      }
+    })
+    var iframeDoc = el.firstChild.contentDocument
+    trigger(iframeDoc, 'click')
+    expect(spy.calls.count()).toBe(1)
+    vm.$destroy()
+    trigger(iframeDoc, 'click')
+    expect(spy.calls.count()).toBe(1)
+    document.body.removeChild(el)
+  })
+
+  it('passing $event', function () {
+    var test = jasmine.createSpy()
+    new Vue({
+      el: el,
+      template: '<a v-on:click="test($event)"></a>',
+      methods: {
+        test: test
+      }
+    })
+    var e = trigger(el.firstChild, 'click')
+    expect(test).toHaveBeenCalledWith(e)
+  })
+
+  it('passing $event on a nested instance', function () {
+    var test = jasmine.createSpy()
+    var parent = new Vue({
+      methods: {
+        test: test
+      }
+    })
+    var child = new Vue({
+      el: el,
+      template: '<a v-on:click="$parent.test($event)"></a>',
+      parent: parent
+    })
+    var e = trigger(child.$el.firstChild, 'click')
+    expect(test).toHaveBeenCalledWith(e)
+  })
+})
diff --git a/test/unit/specs/directives/public/pre_spec.js b/test/unit/specs/directives/public/pre_spec.js
new file mode 100644
index 00000000000..0f3b8c0682c
--- /dev/null
+++ b/test/unit/specs/directives/public/pre_spec.js
@@ -0,0 +1,27 @@
+var Vue = require('src')
+
+describe('v-pre', function () {
+  it('should work', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<div v-pre>{{a}}</div>',
+      data: {
+        a: 123
+      }
+    })
+    expect(vm.$el.firstChild.textContent).toBe('{{a}}')
+  })
+
+  it('should work on root node', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<div v-pre>{{a}}</div>',
+      replace: true,
+      data: {
+        a: 123
+      }
+    })
+    expect(vm.$el.textContent).toBe('{{a}}')
+    expect(getWarnCount()).toBe(0)
+  })
+})
diff --git a/test/unit/specs/directives/public/ref_spec.js b/test/unit/specs/directives/public/ref_spec.js
new file mode 100644
index 00000000000..911a41417ce
--- /dev/null
+++ b/test/unit/specs/directives/public/ref_spec.js
@@ -0,0 +1,145 @@
+var _ = require('src/util')
+var Vue = require('src')
+
+describe('ref', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+  })
+
+  var components = {
+    test: {
+      id: 'test'
+    },
+    test2: {
+      id: 'test2'
+    }
+  }
+
+  it('normal', function () {
+    var vm = new Vue({
+      el: el,
+      components: components,
+      data: {
+        ref: 'test2'
+      },
+      template: '<test v-ref:test></test><test2 v-ref:test-case></test2>'
+    })
+    expect(vm.$refs.test).toBeTruthy()
+    expect(vm.$refs.test.$options.id).toBe('test')
+    expect(vm.$refs.testCase).toBeTruthy()
+    expect(vm.$refs.testCase.$options.id).toBe('test2')
+  })
+
+  it('with dynamic component', function (done) {
+    var vm = new Vue({
+      el: el,
+      components: components,
+      data: { test: 'test' },
+      template: '<component :is="test" v-ref:test></component>'
+    })
+    expect(vm.$refs.test.$options.id).toBe('test')
+    vm.test = 'test2'
+    _.nextTick(function () {
+      expect(vm.$refs.test.$options.id).toBe('test2')
+      vm.test = ''
+      _.nextTick(function () {
+        expect(vm.$refs.test).toBeNull()
+        done()
+      })
+    })
+  })
+
+  it('with dynamic component + keep-alive', function (done) {
+    var vm = new Vue({
+      el: el,
+      components: components,
+      data: { test: 'test' },
+      template: '<component :is="test" v-ref:test keep-alive></component>'
+    })
+    expect(vm.$refs.test.$options.id).toBe('test')
+    vm.test = 'test2'
+    _.nextTick(function () {
+      expect(vm.$refs.test.$options.id).toBe('test2')
+      vm.test = ''
+      _.nextTick(function () {
+        expect(vm.$refs.test).toBe(null)
+        done()
+      })
+    })
+  })
+
+  it('should be reactive when bound by dynamic component and hoisted', function (done) {
+    var vm = new Vue({
+      el: el,
+      data: { view: 'one' },
+      template:
+        '{{$refs.test.value}}' +
+        '<component :is="view" v-ref:test></component>' +
+        '<div v-if="$refs.test.value > 1">ok</div>',
+      components: {
+        one: {
+          id: 'one',
+          replace: true,
+          data: function () {
+            return { value: 1 }
+          }
+        },
+        two: {
+          id: 'two',
+          replace: true,
+          data: function () {
+            return { value: 2 }
+          }
+        }
+      }
+    })
+    expect(vm.$refs.test.$options.id).toBe('one')
+    expect(el.textContent).toBe('1')
+    vm.view = 'two'
+    _.nextTick(function () {
+      expect(vm.$refs.test.$options.id).toBe('two')
+      expect(el.textContent).toBe('2ok')
+      vm.view = ''
+      _.nextTick(function () {
+        expect(vm.$refs.test).toBeNull()
+        expect(el.textContent).toBe('')
+        done()
+      })
+    })
+  })
+
+  // #1147
+  it('should be able to reference host via ref inside transclusion content', function (done) {
+    var vm = new Vue({
+      el: el,
+      template:
+        '<div>' +
+          '<comp v-ref:out>{{$refs.out.msg}}</comp>' +
+        '</div>',
+      components: {
+        comp: {
+          template: '<slot></slot>',
+          data: function () {
+            return { msg: 'foo' }
+          }
+        }
+      }
+    })
+    expect(getWarnCount()).toBe(0)
+    expect(vm.$el.textContent).toBe('foo')
+    vm.$children[0].msg = 'bar'
+    _.nextTick(function () {
+      expect(vm.$el.textContent).toBe('bar')
+      done()
+    })
+  })
+
+  it('warn when used on non-component node', function () {
+    new Vue({
+      el: el,
+      template: '<div v-ref:test></div>'
+    })
+    expect('must be used on a child component').toHaveBeenWarned()
+  })
+})
diff --git a/test/unit/specs/directives/public/show_spec.js b/test/unit/specs/directives/public/show_spec.js
new file mode 100644
index 00000000000..2f988932eff
--- /dev/null
+++ b/test/unit/specs/directives/public/show_spec.js
@@ -0,0 +1,52 @@
+var Vue = require('src')
+var transition = require('src/transition')
+var def = require('src/directives/public/show')
+
+describe('v-show', function () {
+  var el
+  beforeEach(function () {
+    el = document.createElement('div')
+    document.body.appendChild(el)
+    spyOn(transition, 'applyTransition').and.callThrough()
+  })
+
+  afterEach(function () {
+    document.body.removeChild(el)
+  })
+
+  it('should work', function () {
+    var dir = {
+      el: el,
+      update: def.update,
+      apply: def.apply,
+      vm: new Vue()
+    }
+    dir.update(false)
+    expect(el.style.display).toBe('none')
+    dir.update(true)
+    expect(el.style.display).toBe('')
+    expect(transition.applyTransition).toHaveBeenCalled()
+  })
+
+  it('should work with v-else', function (done) {
+    var vm = new Vue({
+      el: el,
+      template:
+        '<p v-show="ok">YES</p>' +
+        '<p v-else>NO</p>',
+      data: {
+        ok: true
+      }
+    })
+    expect(el.children[0].style.display).toBe('')
+    expect(el.children[1].style.display).toBe('none')
+    expect(transition.applyTransition.calls.count()).toBe(2)
+    vm.ok = false
+    Vue.nextTick(function () {
+      expect(el.children[0].style.display).toBe('none')
+      expect(el.children[1].style.display).toBe('')
+      expect(transition.applyTransition.calls.count()).toBe(4)
+      done()
+    })
+  })
+})
diff --git a/test/unit/specs/directives/public/text_spec.js b/test/unit/specs/directives/public/text_spec.js
new file mode 100644
index 00000000000..e3d973a0fcd
--- /dev/null
+++ b/test/unit/specs/directives/public/text_spec.js
@@ -0,0 +1,28 @@
+var _ = require('src/util')
+var def = require('src/directives/public/text')
+
+describe('v-text', function () {
+  it('element', function () {
+    var dir = {
+      el: document.createElement('div')
+    }
+    _.extend(dir, def)
+    dir.bind()
+    dir.update('foo')
+    expect(dir.el.textContent).toBe('foo')
+    dir.update(123)
+    expect(dir.el.textContent).toBe('123')
+  })
+
+  it('text node', function () {
+    var dir = {
+      el: document.createTextNode(' ')
+    }
+    _.extend(dir, def)
+    dir.bind()
+    dir.update('foo')
+    expect(dir.el.nodeValue).toBe('foo')
+    dir.update(123)
+    expect(dir.el.nodeValue).toBe('123')
+  })
+})
diff --git a/test/unit/specs/filters/filters_spec.js b/test/unit/specs/filters/filters_spec.js
new file mode 100644
index 00000000000..532eefb72db
--- /dev/null
+++ b/test/unit/specs/filters/filters_spec.js
@@ -0,0 +1,325 @@
+var filters = require('src/filters')
+
+describe('Filters', function () {
+  it('json read', function () {
+    var filter = filters.json.read
+    var obj = {a: {b: 2}}
+    expect(filter(obj)).toBe(JSON.stringify(obj, null, 2))
+    expect(filter(obj, 4)).toBe(JSON.stringify(obj, null, 4))
+    expect(filter(obj, 0)).toBe(JSON.stringify(obj, null, 0))
+    // plain string
+    expect(filter('1234')).toBe('1234')
+  })
+
+  it('json write', function () {
+    var filter = filters.json.write
+    var obj = '{"a":{"b":2}}'
+    expect(JSON.stringify(filter(obj))).toBe(obj)
+    // error condition
+    var invalidJSON = '{"a":}'
+    expect(filter(invalidJSON)).toBe(invalidJSON)
+  })
+
+  it('capitalize', function () {
+    var filter = filters.capitalize
+    var res = filter('fsefsfsef')
+    expect(res.charAt(0)).toBe('F')
+    expect(res.slice(1)).toBe('sefsfsef')
+    assertNumberAndFalsy(filter)
+  })
+
+  it('uppercase', function () {
+    var filter = filters.uppercase
+    expect(filter('fsefef')).toBe('FSEFEF')
+    assertNumberAndFalsy(filter)
+  })
+
+  it('lowercase', function () {
+    var filter = filters.lowercase
+    expect(filter('AWEsoME')).toBe('awesome')
+    assertNumberAndFalsy(filter)
+  })
+
+  it('pluralize', function () {
+    var filter = filters.pluralize
+    // single arg
+    var arg = 'item'
+    expect(filter(0, arg)).toBe('items')
+    expect(filter(1, arg)).toBe('item')
+    expect(filter(2, arg)).toBe('items')
+    // multi args
+    expect(filter(0, 'st', 'nd', 'rd', 'th')).toBe('th')
+    expect(filter(1, 'st', 'nd', 'rd', 'th')).toBe('st')
+    expect(filter(2, 'st', 'nd', 'rd', 'th')).toBe('nd')
+    expect(filter(3, 'st', 'nd', 'rd', 'th')).toBe('rd')
+    expect(filter(4, 'st', 'nd', 'rd', 'th')).toBe('th')
+    // multi args where selected argument is empty string
+    expect(filter(1, '', 'nd', 'rd', 'th')).toBe('')
+  })
+
+  it('currency', function () {
+    var filter = filters.currency
+    // default
+    expect(filter(1234)).toBe('$1,234.00')
+    expect(filter(1234.45)).toBe('$1,234.45')
+    expect(filter(123443434.4343434)).toBe('$123,443,434.43')
+    expect(filter(0.99)).toBe('$0.99')
+    expect(filter(0.99999)).toBe('$1.00')
+    expect(filter(0.76)).toBe('$0.76')
+    // sign arg
+    expect(filter(2134, '@')).toBe('@2,134.00')
+    // no symbol
+    expect(filter(2134, '')).toBe('2,134.00')
+    // decimal places
+    expect(filter(1234, '$', 0)).toBe('$1,234')
+    // if decimal places are present, currency is required
+    expect(filter(1234, '', 2)).toBe('1,234.00')
+    expect(filter(123.4, '$', 3)).toBe('$123.400')
+    expect(filter(-12345, 'VND', 0)).toBe('-VND12,345')
+    // falsy, infinity and 0
+    expect(filter(0)).toBe('$0.00')
+    expect(filter(false)).toBe('')
+    expect(filter(null)).toBe('')
+    expect(filter(undefined)).toBe('')
+    expect(filter(Infinity)).toBe('')
+    // negative numbers
+    expect(filter(-50)).toBe('-$50.00')
+    expect(filter(-150.43)).toBe('-$150.43')
+    expect(filter(-1500.4343434)).toBe('-$1,500.43')
+  })
+
+  it('debounce', function (done) {
+    var filter = filters.debounce
+    expect(filter(null)).toBeUndefined()
+    var spy = jasmine.createSpy('filter:debounce')
+    var handler = filter(spy)
+    handler()
+    expect(spy).not.toHaveBeenCalled()
+    handler = filter(spy)
+    handler()
+    setTimeout(function () {
+      expect(spy).toHaveBeenCalled()
+    }, 400)
+    var spy2 = jasmine.createSpy('filter:debounce')
+    handler = filter(spy2, 450)
+    handler()
+    handler()
+    setTimeout(function () {
+      expect(spy2).not.toHaveBeenCalled()
+    }, 400)
+    setTimeout(function () {
+      expect(spy2.calls.count()).toBe(1)
+      done()
+    }, 500)
+  })
+
+  it('limitBy', function () {
+    var filter = filters.limitBy
+    var arr = [1, 2, 3]
+    var res = filter(arr, false)
+    expect(res).toBe(arr)
+    res = filter(arr, 1)
+    assertArray(res, [1])
+    res = filter(arr, 10)
+    assertArray(res, [1, 2, 3])
+    res = filter(arr, -1)
+    assertArray(res, [1, 2])
+    // with offsets, note offsets are 0 bound (as expected)
+    res = filter(arr, 1, 1)
+    assertArray(res, [2])
+    res = filter(arr, 2, 1)
+    assertArray(res, [2, 3])
+    res = filter(arr, 1, 2)
+    assertArray(res, [3])
+  })
+
+  it('filterBy', function () {
+    var filter = filters.filterBy
+    var arr = [
+      { a: 1, b: { c: 'hello' }},
+      { a: 2, b: 'hello' },
+      { a: 3, b: ['yoyo'] }
+    ]
+    var res
+    // normal
+    res = filter(arr, 'hello')
+    assertArray(res, [arr[0], arr[1]])
+    // data key
+    res = filter(arr, 'hello', 'b.c')
+    assertArray(res, [arr[0]])
+    // delimiter
+    res = filter(arr, 'hello', 'in', 'b.c')
+    assertArray(res, [arr[0]])
+    // no search key
+    res = filter(arr, null)
+    expect(res).toBe(arr)
+    // number search key
+    res = filter(arr, 2)
+    assertArray(res, [arr[1]])
+    // search in sub array
+    res = filter(arr, 'yoyo')
+    assertArray(res, [arr[2]])
+    // filter by false (#928)
+    arr = [{a: false}, {b: true}]
+    res = filter(arr, false)
+    assertArray(res, [arr[0]])
+    // filter by a function
+    res = filter(arr, function (val) {
+      return val.b === true
+    })
+    assertArray(res, [arr[1]])
+  })
+
+  it('filterBy multiple keys', function () {
+    var filter = filters.filterBy
+    var arr = [
+      { firstname: 'A', lastname: 'B' },
+      { firstname: 'C', lastname: 'B' },
+      { firstname: 'A', lastname: 'D' }
+    ]
+    // multiple string keys
+    var res
+    res = filter(arr, '', 'in', 'firstname', 'lastname')
+    assertArray(res, [arr[0], arr[1], arr[2]])
+    res = filter(arr, 'A', 'in', 'firstname', 'lastname')
+    assertArray(res, [arr[0], arr[2]])
+    // array of keys
+    res = filter(arr, 'B', ['firstname', 'lastname'])
+    assertArray(res, [arr[0], arr[1]])
+    // multiple arrays of keys
+    res = filter(arr, 'C', 'in', ['firstname'], ['lastname'])
+    assertArray(res, [arr[1]])
+    res = filter(arr, 'A', ['firstname', 'lastname'], [])
+    assertArray(res, [arr[0], arr[2]])
+  })
+
+  it('orderBy', function () {
+    var filter = filters.orderBy
+    var arr = [
+      { a: { b: 0 }, c: 'b' },
+      { a: { b: 2 }, c: 'c' },
+      { a: { b: 1 }, c: 'a' }
+    ]
+    var res
+    // sort key
+    res = filter(arr, 'a.b')
+    assertArray(res, [arr[0], arr[2], arr[1]])
+    // reverse key
+    res = filter(arr, 'a.b', -1)
+    assertArray(res, [arr[1], arr[2], arr[0]])
+    // literal asc
+    res = filter(arr, 'c', 1)
+    assertArray(res, [arr[2], arr[0], arr[1]])
+    // no sort key
+    res = filter(arr, null)
+    expect(res).toBe(arr)
+    res = filter(arr)
+    expect(res).toBe(arr)
+  })
+
+  it('orderBy on Object-converted array', function () {
+    // object converted
+    var filter = filters.orderBy
+    var arr = [
+      { $key: 'a', $value: 3 },
+      { $key: 'c', $value: 1 },
+      { $key: 'b', $value: 2 }
+    ]
+    var res = filter(arr, '$key')
+    assertArray(res, [arr[0], arr[2], arr[1]])
+    res = filter(arr, '$value')
+    assertArray(res, [arr[1], arr[2], arr[0]])
+    // normal keys
+    arr = [
+      { $key: 'a', $value: { v: 3 } },
+      { $key: 'c', $value: { v: 1 } },
+      { $key: 'b', $value: { v: 2 } }
+    ]
+    res = filter(arr, 'v')
+    assertArray(res, [arr[1], arr[2], arr[0]])
+  })
+
+  it('orderBy primitive values', function () {
+    var filter = filters.orderBy
+    var arr = [9, 11, 1, 2]
+    var res = filter(arr, true)
+    assertArray(res, [arr[2], arr[3], arr[0], arr[1]])
+  })
+
+  it('orderBy multiple fields', function () {
+    var filter = filters.orderBy
+    var arr = [
+      { a: 1, b: 1, c: 1 }, // 0
+      { a: 0, b: 1, c: 1 }, // 1
+      { a: 1, b: 2, c: 0 }, // 2
+      { a: 1, b: 0, c: 0 }, // 3
+      { a: 0, b: 0, c: 0 }, // 4
+      { a: 0, b: 1, c: 0 }  // 5
+    ]
+    var res
+    // sort two keys
+    res = filter(arr, ['a', 'b'])
+    assertArray(res, [arr[4], arr[1], arr[5], arr[3], arr[0], arr[2]])
+    res = filter(arr, 'a', 'b')
+    assertArray(res, [arr[4], arr[1], arr[5], arr[3], arr[0], arr[2]])
+
+    // sort two keys with order
+    res = filter(arr, ['a', 'b'], 1)
+    assertArray(res, [arr[4], arr[1], arr[5], arr[3], arr[0], arr[2]])
+    res = filter(arr, 'a', 'b', 1)
+    assertArray(res, [arr[4], arr[1], arr[5], arr[3], arr[0], arr[2]])
+
+    // sort three keys
+    res = filter(arr, ['a', 'b', 'c'])
+    assertArray(res, [arr[4], arr[5], arr[1], arr[3], arr[0], arr[2]])
+    res = filter(arr, 'a', 'b', 'c')
+    assertArray(res, [arr[4], arr[5], arr[1], arr[3], arr[0], arr[2]])
+
+    // reverse two key. Preserves order when equal: 1 then 5
+    res = filter(arr, ['a', 'b'], -1)
+    assertArray(res, [arr[2], arr[0], arr[3], arr[1], arr[5], arr[4]])
+    res = filter(arr, 'a', 'b', -1)
+    assertArray(res, [arr[2], arr[0], arr[3], arr[1], arr[5], arr[4]])
+  })
+
+  it('orderBy using a compare function', function () {
+    var filter = filters.orderBy
+    var arr = [9, 11, 1, 2]
+    var res = filter(arr, evenBeforeOdd)
+    assertArray(res, [arr[3], arr[2], arr[0], arr[1]])
+    res = filter(arr, evenBeforeOdd, 1)
+    assertArray(res, [arr[3], arr[2], arr[0], arr[1]])
+    res = filter(arr, evenBeforeOdd, -1)
+    assertArray(res, [arr[1], arr[0], arr[2], arr[3]])
+  })
+})
+
+function evenBeforeOdd (a, b) {
+  if (a % 2 === 0) {
+    if (b % 2 === 0) {
+      return a - b
+    } else {
+      return -1
+    }
+  } else if (b % 2 === 0) {
+    return 1
+  } else {
+    return a - b
+  }
+}
+
+function assertArray (res, expectations) {
+  expect(res.length).toBe(expectations.length)
+  expectations.forEach(function (exp, i) {
+    expect(exp).toBe(res[i])
+  })
+}
+
+function assertNumberAndFalsy (filter) {
+  // should stringify numbers
+  expect(filter(12345)).toBe('12345')
+  expect(filter(0)).toBe('0')
+  expect(filter(undefined)).toBe('')
+  expect(filter(null)).toBe('')
+  expect(filter(false)).toBe('')
+}
diff --git a/test/unit/specs/global_api_spec.js b/test/unit/specs/global_api_spec.js
new file mode 100644
index 00000000000..6a0aeb65cda
--- /dev/null
+++ b/test/unit/specs/global_api_spec.js
@@ -0,0 +1,125 @@
+var Vue = require('src')
+var _ = require('src/util')
+var config = require('src/config')
+var transition = require('src/transition')
+
+describe('Global API', function () {
+  it('exposed utilities', function () {
+    expect(Vue.util).toBe(_)
+    expect(Vue.nextTick).toBe(_.nextTick)
+    expect(Vue.config).toBe(config)
+    expect(Vue.transition.applyTransition).toBe(transition.applyTransition)
+  })
+
+  it('extend', function () {
+    var Test = Vue.extend({
+      name: 'test',
+      a: 1,
+      b: 2
+    })
+    expect(Test.options.a).toBe(1)
+    expect(Test.options.b).toBe(2)
+    expect(Test.super).toBe(Vue)
+    // function.name is not available in IE
+    expect(Test.toString().match(/^function Test\s?\(/)).toBeTruthy()
+    var t = new Test({
+      a: 2
+    })
+    expect(t.$options.a).toBe(2)
+    expect(t.$options.b).toBe(2)
+    // inheritance
+    var Test2 = Test.extend({
+      a: 2
+    })
+    expect(Test2.options.a).toBe(2)
+    expect(Test2.options.b).toBe(2)
+    var t2 = new Test2({
+      a: 3
+    })
+    expect(t2.$options.a).toBe(3)
+    expect(t2.$options.b).toBe(2)
+  })
+
+  it('extend warn invalid names', function () {
+    Vue.extend({ name: '123' })
+    expect('Invalid component name: "123"').toHaveBeenWarned()
+    Vue.extend({ name: '_fesf' })
+    expect('Invalid component name: "_fesf"').toHaveBeenWarned()
+    Vue.extend({ name: 'Some App' })
+    expect('Invalid component name: "Some App"').toHaveBeenWarned()
+  })
+
+  it('use', function () {
+    var def = {}
+    var options = {}
+    var pluginStub = {
+      install: function (Vue, opts) {
+        Vue.directive('plugin-test', def)
+        expect(opts).toBe(options)
+      }
+    }
+    Vue.use(pluginStub, options)
+    expect(Vue.options.directives['plugin-test']).toBe(def)
+    delete Vue.options.directives['plugin-test']
+    // use a function
+    Vue.use(pluginStub.install, options)
+    expect(Vue.options.directives['plugin-test']).toBe(def)
+    delete Vue.options.directives['plugin-test']
+  })
+
+  it('global mixin', function () {
+    var options = Vue.options
+    var spy = jasmine.createSpy('global mixin')
+    Vue.mixin({
+      created: function () {
+        spy(this.$options.myOption)
+      }
+    })
+    new Vue({
+      myOption: 'hello'
+    })
+    expect(spy).toHaveBeenCalledWith('hello')
+    Vue.options = options
+  })
+
+  describe('Asset registration', function () {
+    var Test = Vue.extend()
+
+    it('directive / elementDirective / filter / transition', function () {
+      var assets = ['directive', 'elementDirective', 'filter', 'transition']
+      assets.forEach(function (type) {
+        var def = {}
+        Test[type]('test', def)
+        expect(Test.options[type + 's'].test).toBe(def)
+        expect(Test[type]('test')).toBe(def)
+        // extended registration should not pollute global
+        expect(Vue.options[type + 's'].test).toBeUndefined()
+      })
+    })
+
+    it('component', function () {
+      var def = { a: 1 }
+      Test.component('test', def)
+      var component = Test.options.components.test
+      expect(typeof component).toBe('function')
+      expect(component.super).toBe(Vue)
+      expect(component.options.a).toBe(1)
+      expect(component.options.name).toBe('test')
+      expect(Test.component('test')).toBe(component)
+      // already extended
+      Test.component('test2', component)
+      expect(Test.component('test2')).toBe(component)
+      // extended registration should not pollute global
+      expect(Vue.options.components.test).toBeUndefined()
+    })
+
+    // GitHub issue #3039
+    it('component with `name` option', function () {
+      var def = { name: 'Component1' }
+      Test.component('ns-tree', def)
+      var component = Test.options.components['ns-tree']
+      expect(typeof component).toBe('function')
+      expect(component.options.name).toBe('Component1')
+    })
+  })
+})
diff --git a/test/unit/specs/index.js b/test/unit/specs/index.js
new file mode 100644
index 00000000000..499c94ae2ca
--- /dev/null
+++ b/test/unit/specs/index.js
@@ -0,0 +1,79 @@
+// set some global Vue options
+var Vue = require('src')
+Vue.options.replace = false
+Vue.config.silent = true
+
+/**
+ * Because Vue's internal modules reference the warn function
+ * from different modules (some from util and some from debug),
+ * we need to normalize the warn check into a few global
+ * utility functions.
+ */
+
+var _ = require('src/util')
+var __ = require('src/util/debug')
+var scope = typeof window === 'undefined'
+  ? global
+  : window
+
+scope.getWarnCount = function () {
+  return _.warn.calls.count() + __.warn.calls.count()
+}
+
+function hasWarned (msg) {
+  var count = _.warn.calls.count()
+  var args
+  while (count--) {
+    args = _.warn.calls.argsFor(count)
+    if (args.some(containsMsg)) {
+      return true
+    }
+  }
+
+  count = __.warn.calls.count()
+  while (count--) {
+    args = __.warn.calls.argsFor(count)
+    if (args.some(containsMsg)) {
+      return true
+    }
+  }
+
+  function containsMsg (arg) {
+    if (arg instanceof Error) throw arg
+    return typeof arg === 'string' && arg.indexOf(msg) > -1
+  }
+}
+
+// define custom matcher for warnings
+beforeEach(function () {
+  spyOn(_, 'warn')
+  spyOn(__, 'warn')
+  jasmine.addMatchers({
+    toHaveBeenWarned: function () {
+      return {
+        compare: function (msg) {
+          var warned = Array.isArray(msg)
+            ? msg.some(hasWarned)
+            : hasWarned(msg)
+          return {
+            pass: warned,
+            message: warned
+              ? 'Expected message "' + msg + '" not to have been warned'
+              : 'Expected message "' + msg + '" to have been warned'
+          }
+        }
+      }
+    }
+  })
+})
+
+// shim process
+scope.process = {
+  env: {
+    NODE_ENV: 'development'
+  }
+}
+
+// require all test files
+var testsContext = require.context('.', true, /_spec$/)
+testsContext.keys().forEach(testsContext)
diff --git a/test/unit/specs/instance/events_spec.js b/test/unit/specs/instance/events_spec.js
new file mode 100644
index 00000000000..1fdf8ab6a65
--- /dev/null
+++ b/test/unit/specs/instance/events_spec.js
@@ -0,0 +1,337 @@
+var Vue = require('src')
+var _ = require('src/util')
+
+describe('Instance Events', function () {
+  var spy, spy2
+  beforeEach(function () {
+    spy = jasmine.createSpy()
+    spy2 = jasmine.createSpy()
+  })
+
+  describe('option events', function () {
+    it('normal events', function () {
+      var vm = new Vue({
+        events: {
+          test: spy,
+          test2: [spy, spy]
+        }
+      })
+      vm.$emit('test', 123)
+      expect(spy).toHaveBeenCalledWith(123)
+      vm.$emit('test2')
+      expect(spy.calls.count()).toBe(3)
+    })
+
+    it('hook events', function () {
+      new Vue({
+        events: {
+          'hook:created': spy
+        }
+      })
+      expect(spy).toHaveBeenCalled()
+    })
+
+    it('method name strings', function () {
+      var vm = new Vue({
+        events: {
+          test: 'doSomething',
+          test2: 'doSomethingElse'
+        },
+        methods: {
+          doSomething: spy
+        }
+      })
+      vm.$emit('test', 123)
+      expect(spy).toHaveBeenCalledWith(123)
+      vm.$emit('test2')
+      expect('Unknown method').toHaveBeenWarned()
+    })
+  })
+
+  describe('option watchers', function () {
+    it('normal', function (done) {
+      var spyA = jasmine.createSpy()
+      var spyB = jasmine.createSpy()
+      var count = 0
+      var a = {
+        b: { c: 1 }
+      }
+      var vm = new Vue({
+        watch: {
+          'a.b.c': spyA,
+          'b + c': spyB,
+          a: {
+            deep: true,
+            immediate: true,
+            handler: 'test'
+          }
+        },
+        data: {
+          a: a,
+          b: 1,
+          c: 2
+        },
+        methods: {
+          test: function (val) {
+            count++
+            expect(val).toBe(a)
+          }
+        }
+      })
+      vm.a.b.c = 2
+      vm.b = 3
+      vm.c = 4
+      expect(count).toBe(1)
+      _.nextTick(function () {
+        expect(spyA).toHaveBeenCalledWith(2, 1)
+        expect(spyB).toHaveBeenCalledWith(7, 3)
+        expect(count).toBe(2)
+        done()
+      })
+    })
+
+    it('method name strings', function (done) {
+      var spy = jasmine.createSpy()
+      var vm = new Vue({
+        watch: {
+          'a': 'test'
+        },
+        data: {
+          a: 1
+        },
+        methods: {
+          test: spy
+        }
+      })
+      vm.a = 2
+      _.nextTick(function () {
+        expect(spy).toHaveBeenCalledWith(2, 1)
+        done()
+      })
+    })
+  })
+
+  describe('hooks', function () {
+    it('created', function () {
+      var ctx
+      var vm = new Vue({
+        created: function () {
+          // can't assert this === vm here
+          // because the constructor hasn't returned yet
+          ctx = this
+          // should have observed data
+          expect(this._data.__ob__).toBeTruthy()
+          spy()
+        }
+      })
+      expect(ctx).toBe(vm)
+      expect(spy).toHaveBeenCalled()
+    })
+
+    it('beforeDestroy', function () {
+      var vm = new Vue({
+        beforeDestroy: function () {
+          expect(this).toBe(vm)
+          expect(this._isDestroyed).toBe(false)
+          spy()
+        }
+      })
+      vm.$destroy()
+      expect(spy).toHaveBeenCalled()
+    })
+
+    it('destroyed', function () {
+      var vm = new Vue({
+        destroyed: function () {
+          expect(this).toBe(vm)
+          expect(this._isDestroyed).toBe(true)
+          spy()
+        }
+      })
+      vm.$destroy()
+      expect(spy).toHaveBeenCalled()
+    })
+
+    it('beforeCompile', function () {
+      var vm = new Vue({
+        template: '{{a}}',
+        data: { a: 1 },
+        beforeCompile: function () {
+          expect(this).toBe(vm)
+          expect(this.$el).toBe(el)
+          expect(this.$el.textContent).toBe('{{a}}')
+          spy()
+        }
+      })
+      var el = document.createElement('div')
+      vm.$mount(el)
+      expect(spy).toHaveBeenCalled()
+    })
+
+    it('compiled', function () {
+      var vm = new Vue({
+        template: '{{a}}',
+        data: { a: 1 },
+        compiled: function () {
+          expect(this.$el).toBe(el)
+          expect(this.$el.textContent).toBe('1')
+          spy()
+        }
+      })
+      var el = document.createElement('div')
+      vm.$mount(el)
+      expect(spy).toHaveBeenCalled()
+    })
+
+    it('ready', function () {
+      var vm = new Vue({
+        ready: spy
+      })
+      expect(spy).not.toHaveBeenCalled()
+      var el = document.createElement('div')
+      vm.$mount(el)
+      expect(spy).not.toHaveBeenCalled()
+      vm.$appendTo(document.body)
+      expect(spy).toHaveBeenCalled()
+      vm.$remove()
+      // try mounting on something already in dom
+      el = document.createElement('div')
+      document.body.appendChild(el)
+      vm = new Vue({
+        el: el,
+        ready: spy2
+      })
+      expect(spy2).toHaveBeenCalled()
+      vm.$remove()
+    })
+
+    it('compile v-on on child component', function () {
+      var spy2 = jasmine.createSpy()
+      var vm = new Vue({
+        el: document.createElement('div'),
+        template: '<comp v-on:hook:created="onCreated" @ready="onReady" @ok="a++"></comp>',
+        data: {
+          a: 0
+        },
+        methods: {
+          onCreated: spy,
+          onReady: spy
+        },
+        components: {
+          comp: {
+            compiled: function () {
+              this.$emit('ready', 123)
+              this.$emit('ok')
+              this.$parent.onReady = spy2
+              this.$emit('ready', 234)
+            }
+          }
+        }
+      })
+      expect(spy.calls.count()).toBe(2)
+      expect(spy).toHaveBeenCalledWith(123)
+      expect(spy2.calls.count()).toBe(1)
+      expect(spy2).toHaveBeenCalledWith(234)
+      expect(vm.a).toBe(1)
+    })
+
+    it('warn missing handler for child component v-on', function () {
+      new Vue({
+        el: document.createElement('div'),
+        template: '<comp @test="onThat"></comp>',
+        components: {
+          comp: {}
+        }
+      })
+      expect('v-on:test="onThat" expects a function value').toHaveBeenWarned()
+    })
+
+    it('passing $arguments', function () {
+      new Vue({
+        el: document.createElement('div'),
+        template: '<comp @ready="onReady($arguments[1])"></comp>',
+        methods: {
+          onReady: spy
+        },
+        components: {
+          comp: {
+            compiled: function () {
+              this.$emit('ready', 123, 1234)
+            }
+          }
+        }
+      })
+      expect(spy).toHaveBeenCalledWith(1234)
+    })
+
+    describe('attached/detached', function () {
+      it('in DOM', function () {
+        var el = document.createElement('div')
+        var childEl = document.createElement('div')
+        el.appendChild(childEl)
+        document.body.appendChild(el)
+        var parentVm = new Vue({
+          el: el,
+          attached: spy,
+          detached: spy2
+        })
+        var childVm = new Vue({
+          parent: parentVm,
+          el: childEl,
+          attached: spy,
+          detached: spy2
+        })
+        expect(spy.calls.count()).toBe(2)
+        parentVm.$remove()
+        expect(spy2.calls.count()).toBe(2)
+        // child should be already detached
+        // so the hook should not fire again
+        childVm.$remove()
+        expect(spy2.calls.count()).toBe(2)
+      })
+
+      it('create then attach', function () {
+        var el = document.createElement('div')
+        var childEl = document.createElement('div')
+        el.appendChild(childEl)
+        var parentVm = new Vue({
+          el: el,
+          attached: spy,
+          detached: spy2
+        })
+        var childVm = new Vue({
+          parent: parentVm,
+          el: childEl,
+          attached: spy,
+          detached: spy2
+        })
+        parentVm.$appendTo(document.body)
+        expect(spy.calls.count()).toBe(2)
+        // detach child first
+        childVm.$remove()
+        expect(spy2.calls.count()).toBe(1)
+        // should only fire parent detach
+        parentVm.$remove()
+        expect(spy2.calls.count()).toBe(2)
+      })
+
+      it('should not fire on detached child', function () {
+        var el = document.createElement('div')
+        var childEl = document.createElement('div')
+        var parentVm = new Vue({
+          el: el,
+          attached: spy
+        })
+        var childVm = new Vue({
+          parent: parentVm,
+          el: childEl,
+          attached: spy
+        })
+        parentVm.$appendTo(document.body)
+        expect(spy.calls.count()).toBe(1)
+        childVm.$appendTo(el)
+        expect(spy.calls.count()).toBe(2)
+      })
+    })
+  })
+})
diff --git a/test/unit/specs/instance/init_spec.js b/test/unit/specs/instance/init_spec.js
new file mode 100644
index 00000000000..c6291f28406
--- /dev/null
+++ b/test/unit/specs/instance/init_spec.js
@@ -0,0 +1,52 @@
+var Vue = require('src')
+var init = Vue.prototype._init
+
+describe('Instance Init', function () {
+  var stub = {
+    constructor: {
+      options: { a: 1, b: 2 }
+    },
+    _updateRef: jasmine.createSpy(),
+    _initEvents: jasmine.createSpy(),
+    _callHook: jasmine.createSpy(),
+    _initState: jasmine.createSpy(),
+    $mount: jasmine.createSpy()
+  }
+
+  var options = {
+    a: 2,
+    el: {}
+  }
+
+  init.call(stub, options)
+
+  it('should setup properties', function () {
+    expect(stub.$el).toBe(null)
+    expect(stub.$root).toBe(stub)
+    expect(stub.$refs).toBeTruthy()
+    expect(stub.$els).toBeTruthy()
+    expect(stub._watchers).toBeTruthy()
+    expect(stub._directives).toBeTruthy()
+    expect(stub._events).toBeTruthy()
+    expect(stub._eventsCount).toBeTruthy()
+  })
+
+  it('should merge options', function () {
+    expect(stub.$options.a).toBe(2)
+    expect(stub.$options.b).toBe(2)
+  })
+
+  it('should call other init methods', function () {
+    expect(stub._initEvents).toHaveBeenCalled()
+    expect(stub._initState).toHaveBeenCalled()
+    expect(stub._updateRef).toHaveBeenCalled()
+  })
+
+  it('should call created hook', function () {
+    expect(stub._callHook).toHaveBeenCalledWith('created')
+  })
+
+  it('should call $mount when options.el is present', function () {
+    expect(stub.$mount).toHaveBeenCalledWith(stub.$options.el)
+  })
+})
diff --git a/test/unit/specs/instance/misc_spec.js b/test/unit/specs/instance/misc_spec.js
new file mode 100644
index 00000000000..daf9a68c029
--- /dev/null
+++ b/test/unit/specs/instance/misc_spec.js
@@ -0,0 +1,75 @@
+var Vue = require('src')
+
+describe('misc', function () {
+  describe('_applyFilters', function () {
+    var vm = new Vue({
+      data: {
+        msg: 'bar'
+      },
+      filters: {
+        read: function (v, arg) {
+          return v + ' read:' + arg
+        },
+        read2: {
+          read: function (v, arg) {
+            return v + ' read2:' + arg
+          }
+        },
+        write: {
+          write: function (v, oldV) {
+            return v + ' ' + oldV
+          }
+        },
+        duplex1: {
+          read: function (v) {
+            return v.split('').reverse().join('')
+          },
+          write: function (v) {
+            return v.split('').reverse().join('')
+          }
+        },
+        duplex2: {
+          read: function (v) {
+            return v + 'hi'
+          },
+          write: function (v) {
+            return v.replace('hi', '')
+          }
+        }
+      }
+    })
+
+    it('read', function () {
+      var filters = [
+        { name: 'read', args: [{dynamic: false, value: 'foo'}] },
+        { name: 'read2', args: [{dynamic: true, value: 'msg'}] }
+      ]
+      var val = vm._applyFilters('test', null, filters, false)
+      expect(val).toBe('test read:foo read2:bar')
+    })
+
+    it('write', function () {
+      var filters = [
+        { name: 'write' }
+      ]
+      var val = vm._applyFilters('test', 'oldTest', filters, true)
+      expect(val).toBe('test oldTest')
+    })
+
+    it('chained read + write', function () {
+      var filters = [
+        { name: 'duplex1' },
+        { name: 'duplex2' }
+      ]
+      var val = vm._applyFilters('test', 'oldTest', filters)
+      expect(val).toBe('tsethi')
+      val = vm._applyFilters('tsethi', 'oldTest', filters, true)
+      expect(val).toBe('test')
+    })
+
+    it('warn not found', function () {
+      vm._applyFilters('waldo', null, [{name: 'nemo'}])
+      expect('Failed to resolve filter').toHaveBeenWarned()
+    })
+  })
+})
diff --git a/test/unit/specs/instance/state_spec.js b/test/unit/specs/instance/state_spec.js
new file mode 100644
index 00000000000..a34404b6a1b
--- /dev/null
+++ b/test/unit/specs/instance/state_spec.js
@@ -0,0 +1,281 @@
+var Vue = require('src')
+var _ = require('src/util')
+
+describe('Instance state initialization', function () {
+  it('should warn data functions that do not return an object', function () {
+    new Vue({
+      data: function () {}
+    })
+    expect('should return an object').toHaveBeenWarned()
+  })
+
+  it('should initialize data once per strat', function () {
+    var spyOncePerStrat = jasmine.createSpy('called once per strat')
+    var Comp = Vue.extend({
+      data: function () {
+        spyOncePerStrat()
+        return {
+          result: 'false'
+        }
+      }
+    })
+    new Comp({
+      data: function () {
+        spyOncePerStrat()
+        return {
+          result: 'true'
+        }
+      }
+    })
+    expect(spyOncePerStrat.calls.count()).toBe(2)
+  })
+
+  describe('data proxy', function () {
+    var data = {
+      a: 0,
+      b: 0
+    }
+    var vm = new Vue({
+      data: data
+    })
+
+    it('initial', function () {
+      expect(vm.a).toBe(data.a)
+      expect(vm.b).toBe(data.b)
+    })
+
+    it('vm => data', function () {
+      vm.a = 1
+      expect(data.a).toBe(1)
+      expect(vm.a).toBe(data.a)
+    })
+
+    it('data => vm', function () {
+      data.b = 2
+      expect(vm.b).toBe(2)
+      expect(vm.b).toBe(data.b)
+    })
+  })
+
+  describe('$data', function () {
+    it('should initialize props', function () {
+      var vm = new Vue({
+        el: document.createElement('div'),
+        props: ['c']
+      })
+      expect(_.hasOwn(vm, 'c')).toBe(true)
+    })
+
+    it('external prop should overwrite default value', function () {
+      var el = document.createElement('div')
+      el.setAttribute('v-bind:c', '2')
+      el.textContent = '{{c}}'
+      var vm = new Vue({
+        el: el,
+        props: ['c'],
+        data: {
+          c: 1
+        }
+      })
+      expect(vm.c).toBe(2)
+      expect(el.textContent).toBe('2')
+    })
+
+    it('props should be available in data() and create()', function () {
+      var el = document.createElement('div')
+      el.setAttribute(':c', '2')
+      var vm = new Vue({
+        el: el,
+        props: ['c'],
+        data: function () {
+          expect(this.c).toBe(2)
+          return {
+            d: this.c + 1
+          }
+        },
+        created: function () {
+          expect(this.c).toBe(2)
+        }
+      })
+      expect(vm.d).toBe(3)
+    })
+
+    it('replace $data', function () {
+      var vm = new Vue({
+        data: {
+          a: 1
+        }
+      })
+      vm.$data = { b: 2 }
+      // proxy new key
+      expect(vm.b).toBe(2)
+      // unproxy old key that's no longer present
+      expect(_.hasOwn(vm, 'a')).toBe(false)
+    })
+  })
+
+  describe('computed', function () {
+    var spyE = jasmine.createSpy('computed e')
+    var spyF = jasmine.createSpy('cached computed f')
+    var spyCachedWatcher = jasmine.createSpy('cached computed watcher')
+
+    var Test = Vue.extend({
+      computed: {
+        // uncached
+        c: {
+          cache: false,
+          get: function () {
+            return this.a + this.b
+          }
+        },
+        // with setter
+        d: {
+          get: function () {
+            return this.a + this.b
+          },
+          set: function (newVal) {
+            var vals = newVal.split(' ')
+            this.a = vals[0]
+            this.b = vals[1]
+          }
+        },
+        // chained computed
+        e: function () {
+          return this.c + 'e'
+        },
+        // cached
+        f: {
+          get: function () {
+            spyF()
+            return this.ff
+          }
+        },
+        // chained cached
+        g: function () {
+          return this.f + 1
+        },
+        // another cached, for watcher test
+        h: {
+          get: function () {
+            return this.hh
+          }
+        }
+      }
+    })
+
+    var vm = new Test({
+      data: {
+        a: 'a',
+        b: 'b',
+        ff: 0,
+        hh: 0
+      },
+      watch: {
+        e: spyE,
+        h: spyCachedWatcher
+      }
+    })
+
+    it('get', function () {
+      expect(vm.c).toBe('ab')
+      expect(vm.d).toBe('ab')
+      expect(vm.e).toBe('abe')
+    })
+
+    it('set', function (done) {
+      vm.c = 123 // should do nothing
+      vm.d = 'c d'
+      expect(vm.a).toBe('c')
+      expect(vm.b).toBe('d')
+      expect(vm.c).toBe('cd')
+      expect(vm.d).toBe('cd')
+      expect(vm.e).toBe('cde')
+      Vue.nextTick(function () {
+        expect(spyE).toHaveBeenCalledWith('cde', 'abe')
+        done()
+      })
+    })
+
+    it('cached computed', function () {
+      expect(spyF).not.toHaveBeenCalled()
+      var f = vm.f
+      var g = vm.g
+      expect(spyF.calls.count()).toBe(1)
+      expect(f).toBe(0)
+      expect(g).toBe(1)
+      // get again
+      f = vm.f
+      g = vm.g
+      // should not be evaluated again
+      expect(spyF.calls.count()).toBe(1)
+      expect(f).toBe(0)
+      expect(g).toBe(1)
+      // update dep
+      vm.ff = 1
+      f = vm.f
+      g = vm.g
+      expect(spyF.calls.count()).toBe(2)
+      expect(f).toBe(1)
+      expect(g).toBe(2)
+    })
+
+    it('watching cached computed', function (done) {
+      expect(spyCachedWatcher).not.toHaveBeenCalled()
+      vm.hh = 2
+      Vue.nextTick(function () {
+        expect(spyCachedWatcher).toHaveBeenCalledWith(2, 0)
+        done()
+      })
+    })
+
+    it('same definition object bound to different instance', function () {
+      var vm = new Test({
+        data: {
+          a: 'A',
+          b: 'B'
+        }
+      })
+      expect(vm.c).toBe('AB')
+      expect(vm.d).toBe('AB')
+      vm.d = 'C D'
+      expect(vm.a).toBe('C')
+      expect(vm.b).toBe('D')
+      expect(vm.c).toBe('CD')
+      expect(vm.d).toBe('CD')
+      expect(vm.e).toBe('CDe')
+    })
+  })
+
+  describe('methods', function () {
+    it('should work and have correct context', function () {
+      var vm = new Vue({
+        data: {
+          a: 1
+        },
+        methods: {
+          test: function () {
+            expect(this instanceof Vue).toBe(true)
+            return this.a
+          }
+        }
+      })
+      expect(vm.test()).toBe(1)
+    })
+  })
+
+  describe('meta', function () {
+    var vm = new Vue({
+      _meta: {
+        $index: 0,
+        $value: 'test'
+      }
+    })
+
+    it('should define metas only on vm', function () {
+      expect(vm.$index).toBe(0)
+      expect(vm.$value).toBe('test')
+      expect('$index' in vm.$data).toBe(false)
+      expect('$value' in vm.$data).toBe(false)
+    })
+  })
+})
diff --git a/test/unit/specs/misc_spec.js b/test/unit/specs/misc_spec.js
new file mode 100644
index 00000000000..9125f176ed1
--- /dev/null
+++ b/test/unit/specs/misc_spec.js
@@ -0,0 +1,580 @@
+// test cases for edge cases & bug fixes
+var Vue = require('src')
+var _ = Vue.util
+
+describe('Misc', function () {
+  it('should handle directive.bind() altering its childNode structure', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<div v-test>{{test}}</div>',
+      data: {
+        test: 'foo'
+      },
+      directives: {
+        test: {
+          bind: function () {
+            this.el.insertBefore(document.createTextNode('bar '),
+              this.el.firstChild)
+          }
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('bar foo')
+  })
+
+  it('attached/detached hooks for transcluded components', function () {
+    var spy1 = jasmine.createSpy('attached')
+    var spy2 = jasmine.createSpy('detached')
+    var el = document.createElement('div')
+    el.innerHTML = '<outer v-ref:outter><inner></inner></outer>'
+    document.body.appendChild(el)
+
+    var vm = new Vue({
+      el: el,
+      components: {
+        outer: {
+          template: '<slot></slot>'
+        },
+        inner: {
+          template: 'foo',
+          attached: spy1,
+          detached: spy2
+        }
+      }
+    })
+    expect(spy1).toHaveBeenCalled()
+    vm.$refs.outter.$remove()
+    expect(spy2).toHaveBeenCalled()
+  })
+
+  it('v-for on component root node with replace:true', function () {
+    var el = document.createElement('div')
+    var vm = new Vue({
+      el: el,
+      template: '<test></test>',
+      components: {
+        test: {
+          data: function () {
+            return { list: [1, 2, 3] }
+          },
+          template: '<div v-for="n in list">{{n}}</div>',
+          replace: true
+        }
+      }
+    })
+    expect(vm.$el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
+  })
+
+  // #922
+  it('template v-for inside svg', function () {
+    var el = document.createElement('div')
+    new Vue({
+      el: el,
+      template: '<svg><template v-for="n in list"><text>{{n}}</text></template></svg>',
+      data: {
+        list: [1, 2, 3]
+      }
+    })
+    // IE inlines svg namespace
+    var xmlns = /\s?xmlns=".*svg"/
+    expect(el.innerHTML.replace(xmlns, '')).toBe('<svg><text>1</text><text>2</text><text>3</text></svg>')
+  })
+
+  // #1005
+  it('call lifecycle hooks for child components', function () {
+    Vue.options.replace = true
+    var el = document.createElement('div')
+    var logs = []
+    function log (n) {
+      return function () {
+        logs.push(n)
+      }
+    }
+    document.body.appendChild(el)
+    var vm = new Vue({
+      el: el,
+      attached: log(0),
+      ready: log(1),
+      detached: log(2),
+      beforeDestroy: log(3),
+      destroyed: log(4),
+      template: '<div><test></test><test></test></div>',
+      components: {
+        test: {
+          template: '<span>hi</span>',
+          attached: log(5),
+          ready: log(6),
+          detached: log(7),
+          beforeDestroy: log(8),
+          destroyed: log(9)
+        }
+      }
+    })
+    expect(vm.$el.innerHTML).toBe('<span>hi</span><span>hi</span>')
+    expect(logs.join()).toBe('0,5,6,5,6,1')
+    logs = []
+    vm.$destroy(true)
+    expect(logs.join()).toBe('2,7,7,3,8,9,8,9,4')
+    Vue.options.replace = false
+  })
+
+  // #1966
+  it('call lifecycle hooks for child and grandchild components', function () {
+    Vue.options.replace = true
+    var el = document.createElement('div')
+    var logs = []
+    function log (n) {
+      return function () {
+        logs.push(n)
+      }
+    }
+    document.body.appendChild(el)
+    var vm = new Vue({
+      el: el,
+      attached: log(0),
+      ready: log(1),
+      detached: log(2),
+      beforeDestroy: log(3),
+      destroyed: log(4),
+      template: '<div><test></test></div>',
+      components: {
+        test: {
+          attached: log(5),
+          ready: log(6),
+          detached: log(7),
+          beforeDestroy: log(8),
+          destroyed: log(9),
+          template: '<div><test-inner></test-inner></div>',
+          components: {
+            'test-inner': {
+              attached: log(10),
+              ready: log(11),
+              detached: log(12),
+              beforeDestroy: log(13),
+              destroyed: log(14),
+              template: '<span>hi</span>'
+            }
+          }
+
+        }
+      }
+    })
+    expect(vm.$el.innerHTML).toBe('<div><span>hi</span></div>')
+    expect(logs.join()).toBe('0,5,10,11,6,1')
+    logs = []
+    vm.$destroy(true)
+    expect(logs.join()).toBe('2,7,12,3,8,13,14,9,4')
+    Vue.options.replace = false
+  })
+
+  // #1006
+  it('destroyed hook for components inside v-if', function (done) {
+    var spy = jasmine.createSpy('v-if destroyed hook')
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<template v-if="ok"><test></test></template>',
+      data: {
+        ok: true
+      },
+      components: {
+        test: {
+          destroyed: spy
+        }
+      }
+    })
+    vm.ok = false
+    Vue.nextTick(function () {
+      expect(spy).toHaveBeenCalled()
+      done()
+    })
+  })
+
+  it('frozen model, root', function (done) {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '{{msg}}',
+      data: Object.freeze({
+        msg: 'foo'
+      })
+    })
+    expect(vm.$el.textContent).toBe('foo')
+    try { vm.msg = 'bar' } catch (e) {}
+    Vue.nextTick(function () {
+      expect(vm.$el.textContent).toBe('foo')
+      done()
+    })
+  })
+
+  it('frozen model, non-root', function (done) {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '{{msg}} {{frozen.msg}}',
+      data: {
+        msg: 'foo',
+        frozen: Object.freeze({
+          msg: 'frozen'
+        })
+      }
+    })
+    expect(vm.$el.textContent).toBe('foo frozen')
+    vm.msg = 'bar'
+    try {
+      vm.frozen.msg = 'changed'
+    } catch (error) {
+      if (!(error instanceof TypeError)) {
+        throw error
+      }
+    }
+    Vue.nextTick(function () {
+      expect(vm.$el.textContent).toBe('bar frozen')
+      done()
+    })
+  })
+
+  it('should not trigger deep/Array watchers when digesting', function (done) {
+    var spy1 = jasmine.createSpy('deep')
+    var spy2 = jasmine.createSpy('Array')
+    var spy3 = jasmine.createSpy('test')
+    var spy4 = jasmine.createSpy('deep-mutated')
+    var vm = new Vue({
+      el: document.createElement('div'),
+      data: {
+        obj: {},
+        arr: [],
+        obj2: {}
+      },
+      watch: {
+        obj: {
+          handler: spy1,
+          deep: true
+        },
+        arr: spy2,
+        // if the watcher is watching the added value,
+        // it should still trigger properly
+        test: {
+          handler: spy3,
+          deep: true
+        },
+        // if the object is in fact mutated, it should
+        // still trigger.
+        obj2: {
+          handler: spy4,
+          deep: true
+        }
+      }
+    })
+    var test = []
+    var obj2 = vm.obj2
+    vm.$set('test', test)
+    _.set(obj2, 'test', 123)
+    Vue.nextTick(function () {
+      expect(spy1).not.toHaveBeenCalled()
+      expect(spy2).not.toHaveBeenCalled()
+      expect(spy3).toHaveBeenCalledWith(test, undefined)
+      expect(spy4).toHaveBeenCalledWith(obj2, obj2)
+      done()
+    })
+  })
+
+  it('handle interpolated textarea', function (done) {
+    var el = document.createElement('div')
+    el.innerHTML = '<textarea>hello {{msg}}</textarea>'
+    var vm = new Vue({
+      el: el,
+      data: {
+        msg: 'test'
+      }
+    })
+    expect(el.innerHTML).toBe('<textarea>hello test</textarea>')
+    vm.msg = 'world'
+    Vue.nextTick(function () {
+      expect(el.innerHTML).toBe('<textarea>hello world</textarea>')
+      done()
+    })
+  })
+
+  it('nested object $set should trigger parent array notify', function (done) {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '{{items | json}}{{items[0].a}}',
+      data: {
+        items: [{}]
+      }
+    })
+    expect(vm.$el.textContent).toBe(JSON.stringify(vm.items, null, 2))
+    _.set(vm.items[0], 'a', 123)
+    Vue.nextTick(function () {
+      expect(vm.$el.textContent).toBe(JSON.stringify(vm.items, null, 2) + '123')
+      done()
+    })
+  })
+
+  it('warn unkown custom element', function () {
+    new Vue({
+      el: document.createElement('div'),
+      template: '<custom-stuff></custom-stuff>'
+    })
+    expect('Unknown custom element').toHaveBeenWarned()
+  })
+
+  it('prefer bound attributes over static attributes', function (done) {
+    var el = document.createElement('div')
+    var count = 0
+    var expected = [
+      'bound',
+      'bound',
+      'static',
+      'bound',
+      'bound'
+    ]
+    function check (title) {
+      expect(title).toBe(expected[count])
+      count++
+      if (count === 4) {
+        done()
+      }
+    }
+
+    new Vue({
+      el: el,
+      template:
+        '<div>' +
+          '<comp v-bind:title="title"></comp>' +
+          '<comp title="static" v-bind:title="title"></comp>' +
+          '<comp title="static"></comp>' +
+          '<comp :title="title"></comp>' +
+          '<comp title="static" :title="title"></comp>' +
+        '</div>',
+      data: {
+        title: 'bound'
+      },
+      components: {
+        comp: {
+          props: ['title'],
+          created: function () {
+            check(this.title)
+          }
+        }
+      }
+    })
+  })
+
+  it('deep watch for class, style and bind', function (done) {
+    var el = document.createElement('div')
+    var vm = new Vue({
+      el: el,
+      template: '<div :class="classes" :style="styles" v-bind="attrs"></div>',
+      data: {
+        classes: { a: true, b: false },
+        styles: { color: 'red', fontSize: '14px' },
+        attrs: { a: 1, b: 2 }
+      }
+    })
+    var div = el.firstChild
+    expect(div.className).toBe('a')
+    expect(div.style.color).toBe('red')
+    expect(div.style.fontSize).toBe('14px')
+    expect(div.getAttribute('a')).toBe('1')
+    expect(div.getAttribute('b')).toBe('2')
+    vm.classes.b = true
+    vm.styles.color = 'green'
+    vm.attrs.a = 3
+    Vue.nextTick(function () {
+      expect(div.className).toBe('a b')
+      expect(div.style.color).toBe('green')
+      expect(div.style.fontSize).toBe('14px')
+      expect(div.getAttribute('a')).toBe('3')
+      expect(div.getAttribute('b')).toBe('2')
+      done()
+    })
+  })
+
+  it('IE9 class & :class merge during transclusion', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<test class="outer"></test>',
+      components: {
+        test: {
+          replace: true,
+          template: '<div class="static-inner" :class="{\'inner\': true}"></div>'
+        }
+      }
+    })
+    expect(vm.$el.firstChild.className).toBe('static-inner outer inner')
+  })
+
+  it('SVG class interpolation', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<icon class="abc" icon="def"></icon>',
+      components: {
+        icon: {
+          props: ['class', 'icon'],
+          replace: true,
+          template: '<svg class="si-icon {{icon}} {{class}}"><use xlink:href=""></use></svg>'
+        }
+      }
+    })
+    expect(vm.$el.firstChild.getAttribute('class')).toBe('si-icon def abc')
+  })
+
+  // #1960
+  it('class interpolation should preserve transition class', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<div class="{{test}}" transition="test"></div>',
+      data: {
+        test: 'foo'
+      }
+    })
+    expect(vm.$el.firstChild.className).toBe('foo test-transition')
+  })
+
+  it('transclude class merging should skip interpolated class', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<test class="outer-{{test}}"></test>',
+      data: {
+        test: 'foo'
+      },
+      components: {
+        test: {
+          template: '<div class="inner"></div>',
+          replace: true
+        }
+      }
+    })
+    expect(vm.$el.firstChild.className).toBe('outer-foo')
+  })
+
+  // #2163
+  it('slot compilation order with v-if', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template:
+        '<test>' +
+          '<div slot="one">slot1</div>' +
+          'default content' +
+        '</test>',
+      components: {
+        test: {
+          template:
+            '<div>' +
+              '<slot v-if="true"></slot> ' +
+              '<slot name="one"></slot>' +
+            '</div>',
+          replace: true
+        }
+      }
+    })
+    expect(vm.$el.textContent).toBe('default content slot1')
+  })
+
+  // #2426
+  it('class merge untrimmed', function () {
+    expect(function () {
+      new Vue({
+        el: document.createElement('div'),
+        template: '<test class="p1 p2 "></test>',
+        components: {
+          test: {
+            template: '<div class="hi"></div>',
+            replace: true
+          }
+        }
+      })
+    }).not.toThrow()
+  })
+
+  // #2445
+  it('fragment attach hook should check if child is inDoc', function (done) {
+    var el = document.createElement('div')
+    document.body.appendChild(el)
+    var spyParent = jasmine.createSpy('attached parent')
+    var spyChild = jasmine.createSpy('attached child')
+
+    new Vue({
+      el: el,
+      template: '<comp v-for="n in 1"></comp>',
+      components: {
+        comp: {
+          template: '<div><child></child></div>',
+          attached: function () {
+            expect(_.inDoc(this.$el)).toBe(true)
+            spyParent()
+          },
+          activate: function (next) {
+            setTimeout(function () {
+              next()
+              check()
+            }, 100)
+          },
+          components: {
+            child: {
+              template: 'foo',
+              attached: spyChild
+            }
+          }
+        }
+      }
+    })
+
+    function check () {
+      expect(spyParent).toHaveBeenCalled()
+      expect(spyChild).toHaveBeenCalled()
+      done()
+    }
+  })
+
+  // #2500
+  it('template parser tag match should include hyphen', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<div>{{{ test }}}</div>',
+      data: {
+        test: '<image-field></image-field>'
+      }
+    })
+    expect(vm.$el.querySelector('image-field').namespaceURI).not.toMatch(/svg/)
+  })
+
+  // #2657
+  it('template v-for with v-if', function () {
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<div><template v-for="n in 6" v-if="n % 2">{{ n }}</template></div>'
+    })
+    expect(vm.$el.textContent).toBe('135')
+  })
+
+  // #2821
+  it('batcher should keep flushing until all queues are depleted', function (done) {
+    var spy = jasmine.createSpy()
+    var vm = new Vue({
+      el: document.createElement('div'),
+      template: '<test :prop="model"></test>',
+      data: {
+        model: 0,
+        count: 0
+      },
+      watch: {
+        count: function () {
+          this.model++
+        }
+      },
+      components: {
+        test: {
+          props: ['prop'],
+          watch: {
+            prop: spy
+          }
+        }
+      }
+    })
+    vm.count++
+    Vue.nextTick(function () {
+      expect(spy).toHaveBeenCalled()
+      done()
+    })
+  })
+})
diff --git a/test/unit/specs/observer/dep_spec.js b/test/unit/specs/observer/dep_spec.js
new file mode 100644
index 00000000000..70c8d427d94
--- /dev/null
+++ b/test/unit/specs/observer/dep_spec.js
@@ -0,0 +1,32 @@
+var Dep = require('src/observer/dep')
+
+describe('Dep', function () {
+  var d
+  beforeEach(function () {
+    d = new Dep()
+  })
+
+  it('addSub', function () {
+    var sub = {}
+    d.addSub(sub)
+    expect(d.subs.length).toBe(1)
+    expect(d.subs.indexOf(sub)).toBe(0)
+  })
+
+  it('removeSub', function () {
+    var sub = {}
+    d.addSub(sub)
+    d.removeSub(sub)
+    expect(d.subs.length).toBe(0)
+    expect(d.subs.indexOf(sub)).toBe(-1)
+  })
+
+  it('notify', function () {
+    var sub = {
+      update: jasmine.createSpy('sub')
+    }
+    d.addSub(sub)
+    d.notify()
+    expect(sub.update).toHaveBeenCalled()
+  })
+})
diff --git a/test/unit/specs/observer/observer_spec.js b/test/unit/specs/observer/observer_spec.js
new file mode 100644
index 00000000000..ea9ade50aae
--- /dev/null
+++ b/test/unit/specs/observer/observer_spec.js
@@ -0,0 +1,373 @@
+var Vue = require('src')
+var ob = require('src/observer')
+var Observer = ob.Observer
+var observe = ob.observe
+var Dep = require('src/observer/dep')
+var _ = require('src/util')
+
+describe('Observer', function () {
+  it('create on non-observables', function () {
+    // skip primitive value
+    var ob = observe(1)
+    expect(ob).toBeUndefined()
+    // avoid vue instance
+    ob = observe(new Vue())
+    expect(ob).toBeUndefined()
+    // avoid frozen objects
+    ob = observe(Object.freeze({}))
+    expect(ob).toBeUndefined()
+  })
+
+  it('create on object', function () {
+    // on object
+    var obj = {
+      a: {},
+      b: {}
+    }
+    var ob = observe(obj)
+    expect(ob instanceof Observer).toBe(true)
+    expect(ob.value).toBe(obj)
+    expect(obj.__ob__).toBe(ob)
+    // should've walked children
+    expect(obj.a.__ob__ instanceof Observer).toBe(true)
+    expect(obj.b.__ob__ instanceof Observer).toBe(true)
+    // should return existing ob on already observed objects
+    var ob2 = observe(obj)
+    expect(ob2).toBe(ob)
+  })
+
+  it('create on null', function () {
+    // on null
+    var obj = Object.create(null)
+    obj.a = {}
+    obj.b = {}
+    var ob = observe(obj)
+    expect(ob instanceof Observer).toBe(true)
+    expect(ob.value).toBe(obj)
+    expect(obj.__ob__).toBe(ob)
+    // should've walked children
+    expect(obj.a.__ob__ instanceof Observer).toBe(true)
+    expect(obj.b.__ob__ instanceof Observer).toBe(true)
+    // should return existing ob on already observed objects
+    var ob2 = observe(obj)
+    expect(ob2).toBe(ob)
+  })
+
+  it('create on already observed object', function () {
+    // on object
+    var obj = {}
+    var val = 0
+    var getCount = 0
+    Object.defineProperty(obj, 'a', {
+      configurable: true,
+      enumerable: true,
+      get: function () {
+        getCount++
+        return val
+      },
+      set: function (v) {
+        val = v
+      }
+    })
+
+    var ob = observe(obj)
+    expect(ob instanceof Observer).toBe(true)
+    expect(ob.value).toBe(obj)
+    expect(obj.__ob__).toBe(ob)
+
+    getCount = 0
+    // Each read of 'a' should result in only one get underlying get call
+    obj.a
+    expect(getCount).toBe(1)
+    obj.a
+    expect(getCount).toBe(2)
+
+    // should return existing ob on already observed objects
+    var ob2 = observe(obj)
+    expect(ob2).toBe(ob)
+
+    // should call underlying setter
+    obj.a = 10
+    expect(val).toBe(10)
+  })
+
+  it('create on property with only getter', function () {
+    // on object
+    var obj = {}
+    Object.defineProperty(obj, 'a', {
+      configurable: true,
+      enumerable: true,
+      get: function () {
+        return 123
+      }
+    })
+
+    var ob = observe(obj)
+    expect(ob instanceof Observer).toBe(true)
+    expect(ob.value).toBe(obj)
+    expect(obj.__ob__).toBe(ob)
+
+    // should be able to read
+    expect(obj.a).toBe(123)
+
+    // should return existing ob on already observed objects
+    var ob2 = observe(obj)
+    expect(ob2).toBe(ob)
+
+    // since there is no setter, you shouldn't be able to write to it
+    // PhantomJS throws when a property with no setter is set
+    // but other real browsers don't
+    try {
+      obj.a = 101
+    } catch (e) {}
+    expect(obj.a).toBe(123)
+  })
+
+  it('create on property with only setter', function () {
+    // on object
+    var obj = {}
+    var val = 10
+    Object.defineProperty(obj, 'a', { // eslint-disable-line accessor-pairs
+      configurable: true,
+      enumerable: true,
+      set: function (v) {
+        val = v
+      }
+    })
+
+    var ob = observe(obj)
+    expect(ob instanceof Observer).toBe(true)
+    expect(ob.value).toBe(obj)
+    expect(obj.__ob__).toBe(ob)
+
+    // reads should return undefined
+    expect(obj.a).toBe(undefined)
+
+    // should return existing ob on already observed objects
+    var ob2 = observe(obj)
+    expect(ob2).toBe(ob)
+
+    // writes should call the set function
+    obj.a = 100
+    expect(val).toBe(100)
+  })
+
+  it('create on property which is marked not configurable', function () {
+    // on object
+    var obj = {}
+    Object.defineProperty(obj, 'a', {
+      configurable: false,
+      enumerable: true,
+      val: 10
+    })
+
+    var ob = observe(obj)
+    expect(ob instanceof Observer).toBe(true)
+    expect(ob.value).toBe(obj)
+    expect(obj.__ob__).toBe(ob)
+  })
+
+  it('create on array', function () {
+    // on object
+    var arr = [{}, {}]
+    var ob = observe(arr)
+    expect(ob instanceof Observer).toBe(true)
+    expect(ob.value).toBe(arr)
+    expect(arr.__ob__).toBe(ob)
+    // should've walked children
+    expect(arr[0].__ob__ instanceof Observer).toBe(true)
+    expect(arr[1].__ob__ instanceof Observer).toBe(true)
+  })
+
+  it('observing object prop change', function () {
+    var obj = { a: { b: 2 } }
+    observe(obj)
+    // mock a watcher!
+    var watcher = {
+      deps: [],
+      addDep: function (dep) {
+        this.deps.push(dep)
+        dep.addSub(this)
+      },
+      update: jasmine.createSpy()
+    }
+    // collect dep
+    Dep.target = watcher
+    obj.a.b
+    Dep.target = null
+    expect(watcher.deps.length).toBe(3) // obj.a + a.b + b
+    obj.a.b = 3
+    expect(watcher.update.calls.count()).toBe(1)
+    // swap object
+    obj.a = { b: 4 }
+    expect(watcher.update.calls.count()).toBe(2)
+    watcher.deps = []
+    Dep.target = watcher
+    obj.a.b
+    Dep.target = null
+    expect(watcher.deps.length).toBe(3)
+    // set on the swapped object
+    obj.a.b = 5
+    expect(watcher.update.calls.count()).toBe(3)
+  })
+
+  it('observing object prop change on defined property', function () {
+    var obj = { val: 2 }
+    Object.defineProperty(obj, 'a', {
+      configurable: true,
+      enumerable: true,
+      get: function () {
+        return this.val
+      },
+      set: function (v) {
+        this.val = v
+        return this.val
+      }
+    })
+
+    observe(obj)
+    // mock a watcher!
+    var watcher = {
+      deps: [],
+      addDep: function (dep) {
+        this.deps.push(dep)
+        dep.addSub(this)
+      },
+      update: jasmine.createSpy()
+    }
+    // collect dep
+    Dep.target = watcher
+    expect(obj.a).toBe(2) // Make sure 'this' is preserved
+    Dep.target = null
+    obj.a = 3
+    expect(obj.val).toBe(3) // make sure 'setter' was called
+    obj.val = 5
+    expect(obj.a).toBe(5) // make sure 'getter' was called
+  })
+
+  it('observing set/delete', function () {
+    var obj = { a: 1 }
+    var ob = observe(obj)
+    var dep = ob.dep
+    spyOn(dep, 'notify')
+    _.set(obj, 'b', 2)
+    expect(obj.b).toBe(2)
+    expect(dep.notify.calls.count()).toBe(1)
+    _.del(obj, 'a')
+    expect(_.hasOwn(obj, 'a')).toBe(false)
+    expect(dep.notify.calls.count()).toBe(2)
+    // set existing key, should be a plain set and not
+    // trigger own ob's notify
+    _.set(obj, 'b', 3)
+    expect(obj.b).toBe(3)
+    expect(dep.notify.calls.count()).toBe(2)
+    // set non-existing key
+    _.set(obj, 'c', 1)
+    expect(obj.c).toBe(1)
+    expect(dep.notify.calls.count()).toBe(3)
+    // should ignore deleting non-existing key
+    _.del(obj, 'a')
+    expect(dep.notify.calls.count()).toBe(3)
+    // should work on non-observed objects
+    var obj2 = { a: 1 }
+    _.del(obj2, 'a')
+    expect(_.hasOwn(obj2, 'a')).toBe(false)
+    // should work on Object.create(null)
+    var obj3 = Object.create(null)
+    obj3.a = 1
+    var ob3 = observe(obj3)
+    var dep3 = ob3.dep
+    spyOn(dep3, 'notify')
+    _.set(obj3, 'b', 2)
+    expect(obj3.b).toBe(2)
+    expect(dep3.notify.calls.count()).toBe(1)
+    _.del(obj3, 'a')
+    expect(_.hasOwn(obj3, 'a')).toBe(false)
+    expect(dep3.notify.calls.count()).toBe(2)
+  })
+
+  it('observing set/delete in Vm object', function (done) {
+    var el = document.createElement('div')
+    var vm = new Vue({
+      el: el,
+      template: '<div>{{a}}</div>',
+      data: { a: 1 }
+    })
+    expect(el.innerHTML).toBe('<div>1</div>')
+    Vue.set(vm, 'a', 2)
+    Vue.nextTick(function () {
+      expect(el.innerHTML).toBe('<div>2</div>')
+      Vue.delete(vm, 'a')
+      Vue.nextTick(function () {
+        expect(el.innerHTML).toBe('<div></div>')
+        done()
+      })
+    })
+  })
+
+  it('observing array mutation', function () {
+    var arr = []
+    var ob = observe(arr)
+    var dep = ob.dep
+    spyOn(dep, 'notify')
+    var objs = [{}, {}, {}]
+    arr.push(objs[0])
+    arr.pop()
+    arr.unshift(objs[1])
+    arr.shift()
+    arr.splice(0, 0, objs[2])
+    arr.sort()
+    arr.reverse()
+    expect(dep.notify.calls.count()).toBe(7)
+    // inserted elements should be observed
+    objs.forEach(function (obj) {
+      expect(obj.__ob__ instanceof Observer).toBe(true)
+    })
+  })
+
+  it('array $set', function () {
+    var arr = [1]
+    var ob = observe(arr)
+    var dep = ob.dep
+    spyOn(dep, 'notify')
+    arr.$set(0, 2)
+    expect(arr[0]).toBe(2)
+    expect(dep.notify.calls.count()).toBe(1)
+    // setting out of bound index
+    arr.$set(2, 3)
+    expect(arr[2]).toBe(3)
+    expect(dep.notify.calls.count()).toBe(2)
+  })
+
+  it('array $remove', function () {
+    var arr = [{}, {}]
+    var obj1 = arr[0]
+    var obj2 = arr[1]
+    var ob = observe(arr)
+    var dep = ob.dep
+    spyOn(dep, 'notify')
+    // remove by identity, not in array
+    arr.$remove(obj1)
+    expect(arr.length).toBe(1)
+    expect(arr[0]).toBe(obj2)
+    expect(dep.notify.calls.count()).toBe(1)
+    // remove by identity, in array
+    arr.$remove(obj2)
+    expect(arr.length).toBe(0)
+    expect(dep.notify.calls.count()).toBe(2)
+  })
+
+  it('no proto', function () {
+    _.hasProto = false
+    var arr = [1, 2, 3]
+    var ob2 = observe(arr)
+    expect(arr.$set).toBeTruthy()
+    expect(arr.$remove).toBeTruthy()
+    expect(arr.push).not.toBe([].push)
+    var dep2 = ob2.dep
+    spyOn(dep2, 'notify')
+    arr.push(1)
+    expect(dep2.notify).toHaveBeenCalled()
+    _.hasProto = true
+  })
+})
diff --git a/test/unit/specs/parsers/directive_spec.js b/test/unit/specs/parsers/directive_spec.js
new file mode 100644
index 00000000000..1abcc356c1c
--- /dev/null
+++ b/test/unit/specs/parsers/directive_spec.js
@@ -0,0 +1,119 @@
+var parse = require('src/parsers/directive').parseDirective
+
+describe('Directive Parser', function () {
+  it('simple', function () {
+    var res = parse('exp')
+    expect(res.expression).toBe('exp')
+  })
+
+  it('with filters', function () {
+    var res = parse('exp | abc de \'ok\' \'\' 123 | bcd')
+    expect(res.expression).toBe('exp')
+    expect(res.filters.length).toBe(2)
+    expect(res.filters[0].name).toBe('abc')
+    expect(res.filters[0].args.length).toBe(4)
+    expect(res.filters[0].args[0].value).toBe('de')
+    expect(res.filters[0].args[0].dynamic).toBe(true)
+    expect(res.filters[0].args[1].value).toBe('ok')
+    expect(res.filters[0].args[1].dynamic).toBe(false)
+    expect(res.filters[0].args[2].value).toBe('')
+    expect(res.filters[0].args[2].dynamic).toBe(false)
+    expect(res.filters[0].args[3].value).toBe(123)
+    expect(res.filters[0].args[3].dynamic).toBe(false)
+    expect(res.filters[1].name).toBe('bcd')
+    expect(res.filters[1].args).toBeUndefined()
+  })
+
+  it('reserved filter args', function () {
+    var res = parse('arr | filterBy a in b')
+    expect(res.expression).toBe('arr')
+    expect(res.filters.length).toBe(1)
+    expect(res.filters[0].args.length).toBe(3)
+    expect(res.filters[0].args[0].value).toBe('a')
+    expect(res.filters[0].args[0].dynamic).toBe(true)
+    expect(res.filters[0].args[1].value).toBe('in')
+    expect(res.filters[0].args[1].dynamic).toBe(false)
+    expect(res.filters[0].args[2].value).toBe('b')
+    expect(res.filters[0].args[2].dynamic).toBe(true)
+  })
+
+  it('double pipe', function () {
+    var res = parse('a || b | c')
+    expect(res.expression).toBe('a || b')
+    expect(res.filters.length).toBe(1)
+    expect(res.filters[0].name).toBe('c')
+    expect(res.filters[0].args).toBeUndefined()
+  })
+
+  it('single quote + boolean', function () {
+    var res = parse('a ? \'b\' : c')
+    expect(res.expression).toBe('a ? \'b\' : c')
+    expect(res.filters).toBeUndefined()
+  })
+
+  it('double quote + boolean', function () {
+    var res = parse('"a:b:c||d|e|f" || d ? a : b')
+    expect(res.expression).toBe('"a:b:c||d|e|f" || d ? a : b')
+    expect(res.filters).toBeUndefined()
+    expect(res.arg).toBeUndefined()
+  })
+
+  it('nested function calls + array/object literals', function () {
+    var res = parse('test(c.indexOf(d,f),"e,f")')
+    expect(res.expression).toBe('test(c.indexOf(d,f),"e,f")')
+  })
+
+  it('array literal', function () {
+    var res = parse('d || [e,f]')
+    expect(res.expression).toBe('d || [e,f]')
+    expect(res.filters).toBeUndefined()
+  })
+
+  it('object literal', function () {
+    var res = parse('{a: 1, b: 2} | p')
+    expect(res.expression).toBe('{a: 1, b: 2}')
+    expect(res.filters.length).toBe(1)
+    expect(res.filters[0].name).toBe('p')
+    expect(res.filters[0].args).toBeUndefined()
+  })
+
+  it('escape string', function () {
+    var res = parse("'a\\'b' | test")
+    expect(res.expression).toBe("'a\\'b'")
+    expect(res.filters.length).toBe(1)
+    expect(res.filters[0].name).toBe('test')
+    expect(res.filters[0].args).toBeUndefined()
+  })
+
+  it('white spaces inside object literal', function () {
+    var res = parse('abc | filter {a:1} {b: 2}')
+    expect(res.expression).toBe('abc')
+    expect(res.filters.length).toBe(1)
+    expect(res.filters[0].name).toBe('filter')
+    expect(res.filters[0].args.length).toBe(2)
+    expect(res.filters[0].args[0].value).toBe('{a:1}')
+    expect(res.filters[0].args[0].dynamic).toBe(true)
+    expect(res.filters[0].args[1].value).toBe('{b: 2}')
+    expect(res.filters[0].args[1].dynamic).toBe(true)
+  })
+
+  it('white spaces inside array literal', function () {
+    var res = parse('abc | filter0 abc||def | filter1 [ 1, { a: 2 }]')
+    expect(res.expression).toBe('abc')
+    expect(res.filters.length).toBe(2)
+    expect(res.filters[0].name).toBe('filter0')
+    expect(res.filters[0].args.length).toBe(1)
+    expect(res.filters[0].args[0].value).toBe('abc||def')
+    expect(res.filters[0].args[0].dynamic).toBe(true)
+    expect(res.filters[1].name).toBe('filter1')
+    expect(res.filters[1].args.length).toBe(1)
+    expect(res.filters[1].args[0].value).toBe('[ 1, { a: 2 }]')
+    expect(res.filters[1].args[0].dynamic).toBe(true)
+  })
+
+  it('cache', function () {
+    var res1 = parse('a || b | c')
+    var res2 = parse('a || b | c')
+    expect(res1).toBe(res2)
+  })
+})
diff --git a/test/unit/specs/parsers/expression_spec.js b/test/unit/specs/parsers/expression_spec.js
new file mode 100644
index 00000000000..c6981b334c5
--- /dev/null
+++ b/test/unit/specs/parsers/expression_spec.js
@@ -0,0 +1,353 @@
+var expParser = require('src/parsers/expression')
+
+var testCases = [
+  {
+    // simple path
+    exp: 'a.b.d',
+    scope: {
+      a: { b: { d: 123 }}
+    },
+    expected: 123,
+    paths: ['a']
+  },
+  // complex path
+  {
+    exp: 'a["b"].c',
+    scope: {
+      a: { b: { c: 234 }}
+    },
+    expected: 234,
+    paths: ['a']
+  },
+  {
+    // string concat
+    exp: 'a+b',
+    scope: {
+      a: 'hello',
+      b: 'world'
+    },
+    expected: 'helloworld',
+    paths: ['a', 'b']
+  },
+  {
+    // math
+    exp: 'a - b * 2 + 45',
+    scope: {
+      a: 100,
+      b: 23
+    },
+    expected: 100 - 23 * 2 + 45,
+    paths: ['a', 'b']
+  },
+  {
+    // boolean logic
+    exp: '(a && b) ? c : d || e',
+    scope: {
+      a: true,
+      b: false,
+      c: null,
+      d: false,
+      e: 'worked'
+    },
+    expected: 'worked',
+    paths: ['a', 'b', 'c', 'd', 'e']
+  },
+  {
+    // inline string with newline
+    exp: "a + 'hel\nlo'",
+    scope: {
+      a: 'inline '
+    },
+    expected: 'inline hel\nlo',
+    paths: ['a']
+  },
+  {
+    // multiline expressions
+    exp: "{\n a: '35',\n b: c}",
+    scope: {c: 32},
+    expected: { a: '35', b: 32 }
+  },
+  {
+    // Object with string values with back-quotes
+    exp: '[{"a":"he`llo"},{"b":"world"},{"c":55}]',
+    scope: {},
+    expected: [{ 'a': 'he`llo'}, { 'b': 'world'}, { 'c': 55}]
+  },
+  {
+    // Object with string values and back quotes (single quoted string)
+    exp: '[{\'a\':\'he`llo\'},{\'b\':\'world\'},{\'c\':55}]',
+    scope: {},
+    expected: [{ 'a': 'he`llo'}, { 'b': 'world'}, { 'c': 55}]
+  },
+  {
+    // dollar signs and underscore
+    exp: "_a + ' ' + $b",
+    scope: {
+      _a: 'underscore',
+      $b: 'dollar'
+    },
+    expected: 'underscore dollar',
+    paths: ['_a', '$b']
+  },
+  {
+    // complex with nested values
+    exp: "todo.title + ' : ' + (todo['done'] ? 'yep' : 'nope')",
+    scope: {
+      todo: {
+        title: 'write tests',
+        done: false
+      }
+    },
+    expected: 'write tests : nope',
+    paths: ['todo']
+  },
+  {
+    // expression with no data variables
+    exp: "'a' + 'b'",
+    scope: {},
+    expected: 'ab',
+    paths: []
+  },
+  {
+    // values with same variable name inside strings
+    exp: "'\"test\"' + test + \"'hi'\" + hi",
+    scope: {
+      test: 1,
+      hi: 2
+    },
+    expected: '"test"1\'hi\'2',
+    paths: ['test', 'hi']
+  },
+  {
+    // expressions with inline object literals
+    exp: "sortRows({ column: 'name', test: foo, durrr: 123 })",
+    scope: {
+      sortRows: function (params) {
+        return params.column + params.test + params.durrr
+      },
+      foo: 'bar'
+    },
+    expected: 'namebar123',
+    paths: ['sortRows', 'bar']
+  },
+  {
+    // space between path segments
+    exp: '  a    .   b    .  c + d',
+    scope: {
+      a: { b: { c: 12 }},
+      d: 3
+    },
+    expected: 15,
+    paths: ['a', 'd']
+  },
+  {
+    // space in bracket identifiers
+    exp: ' a[ " a.b.c " ] + b  [ \' e \' ]',
+    scope: {
+      a: {' a.b.c ': 123},
+      b: {' e ': 234}
+    },
+    expected: 357,
+    paths: ['a', 'b']
+  },
+  {
+    // number literal
+    exp: 'a * 1e2 + 1.1',
+    scope: {
+      a: 3
+    },
+    expected: 301.1,
+    paths: ['a']
+  },
+  {
+    // keyowrd + keyword literal
+    exp: 'true && a.true',
+    scope: {
+      a: { 'true': false }
+    },
+    expected: false,
+    paths: ['a']
+  },
+  {
+    // super complex
+    exp: ' $a + b[ "  a.b.c  " ][\'123\'].$e&&c[ " d " ].e + Math.round(e) ',
+    scope: {
+      $a: 1,
+      b: {
+        '  a.b.c  ': {
+          '123': { $e: 2 }
+        }
+      },
+      c: { ' d ': {e: 3}},
+      e: 4.5
+    },
+    expected: 8,
+    paths: ['$a', 'b', 'c', 'e']
+  },
+  {
+    // string with escaped quotes
+    exp: "'a\\'b' + c",
+    scope: {
+      c: '\'c'
+    },
+    expected: "a'b'c",
+    paths: ['c']
+  },
+  {
+    // dynamic sub path
+    exp: "a['b' + i + 'c']",
+    scope: {
+      i: 0,
+      a: {
+        'b0c': 123
+      }
+    },
+    expected: 123,
+    paths: ['a', 'i']
+  },
+  {
+    // Math global, simple path
+    exp: 'Math.PI',
+    scope: {},
+    expected: Math.PI,
+    paths: []
+  },
+  {
+    // Math global, exp
+    exp: 'Math.sin(a)',
+    scope: {
+      a: 1
+    },
+    expected: Math.sin(1),
+    paths: ['a']
+  },
+  {
+    // boolean literal
+    exp: 'true',
+    scope: {
+      true: false
+    },
+    expected: true,
+    paths: []
+  },
+  {
+    exp: 'null',
+    scope: {},
+    expected: null,
+    paths: []
+  },
+  {
+    exp: 'undefined',
+    scope: { undefined: 1 },
+    expected: undefined,
+    paths: []
+  },
+  {
+    // Date global
+    exp: 'Date.now() > new Date("2000-01-01")',
+    scope: {},
+    expected: true,
+    paths: []
+  },
+  // typeof operator
+  {
+    exp: 'typeof test === "string"',
+    scope: { test: '123' },
+    expected: true,
+    paths: ['test']
+  },
+  // isNaN
+  {
+    exp: 'isNaN(a)',
+    scope: { a: 2 },
+    expected: false,
+    paths: ['a']
+  },
+  // parseFloat & parseInt
+  {
+    exp: 'parseInt(a, 10) + parseFloat(b)',
+    scope: { a: 2.33, b: '3.45' },
+    expected: 5.45,
+    paths: ['a', 'b']
+  }
+]
+
+describe('Expression Parser', function () {
+  testCases.forEach(function (testCase) {
+    it('parse getter: ' + testCase.exp, function () {
+      var res = expParser.parseExpression(testCase.exp, true)
+      expect(res.get(testCase.scope)).toEqual(testCase.expected)
+    })
+  })
+
+  it('dynamic setter', function () {
+    // make sure checkSetter works:
+    // should add setter if a cache hit doesn't have hit function.
+    expParser.parseExpression('a[b]')
+    var res = expParser.parseExpression('a[b]', true)
+    var scope = {
+      a: { c: 1 },
+      b: 'c'
+    }
+    res.set(scope, 2)
+    expect(scope.a.c).toBe(2)
+  })
+
+  it('simple path setter', function () {
+    var res = expParser.parseExpression('a.b.c', true)
+    var scope = {}
+    expect(function () {
+      res.set(scope, 123)
+    }).not.toThrow()
+    scope.a = {b: {c: 0}}
+    res.set(scope, 123)
+    expect(scope.a.b.c).toBe(123)
+  })
+
+  it('cache', function () {
+    var res1 = expParser.parseExpression('a + b')
+    var res2 = expParser.parseExpression('a + b')
+    expect(res1).toBe(res2)
+  })
+
+  if (canMakeTemplateStringFunction()) {
+    it('ES2015 template string handling', function () {
+      var res = expParser.parseExpression('a + `hi ${ b }` + c')
+      expect(res.get.toString().indexOf('scope.a+`hi ${scope.b}`+scope.c') > -1).toBe(true)
+      res = expParser.parseExpression('`hi ${ b + `${ d }` }`')
+      expect(res.get.toString().indexOf('`hi ${scope.b+`${scope.d}`}`') > -1).toBe(true)
+      res = expParser.parseExpression('{transform:`rotate(${x}deg)`}')
+      expect(res.get.toString().indexOf('{transform:`rotate(${scope.x}deg)`}') > -1).toBe(true)
+    })
+  }
+
+  describe('invalid expression', function () {
+    it('should warn on invalid expression', function () {
+      expect(getWarnCount()).toBe(0)
+      expParser.parseExpression('a--b"ffff')
+      expect('Invalid expression').toHaveBeenWarned()
+    })
+
+    it('should warn on invalid setter expression', function () {
+      expect(getWarnCount()).toBe(0)
+      expParser.parseExpression('a+b', true)
+      expect('Invalid setter expression').toHaveBeenWarned()
+    })
+
+    it('should warn if expression contains improper reserved keywords', function () {
+      expect(getWarnCount()).toBe(0)
+      expParser.parseExpression('break + 1')
+      expect('Avoid using reserved keywords').toHaveBeenWarned()
+    })
+  })
+})
+
+function canMakeTemplateStringFunction () {
+  try {
+    /* eslint-disable no-new-func */
+    new Function('a', 'return `${a}`')
+  } catch (e) {
+    return false
+  }
+  return true
+}
diff --git a/test/unit/specs/parsers/path_spec.js b/test/unit/specs/parsers/path_spec.js
new file mode 100644
index 00000000000..5e5eec47734
--- /dev/null
+++ b/test/unit/specs/parsers/path_spec.js
@@ -0,0 +1,165 @@
+var Path = require('src/parsers/path')
+var _ = require('src/util')
+
+function assertPath (str, expected) {
+  var path = Path.parsePath(str)
+  var res = pathMatch(path, expected)
+  expect(res).toBe(true)
+  if (!res) {
+    console.log('Path parse failed: ', str, path)
+  }
+}
+
+function assertInvalidPath (str) {
+  var path = Path.parsePath(str)
+  expect(path).toBeUndefined()
+}
+
+function pathMatch (a, b) {
+  if (a.length !== b.length) {
+    return false
+  }
+  for (var i = 0; i < a.length; i++) {
+    if (a[i] !== b[i]) {
+      return false
+    }
+  }
+  return true
+}
+
+describe('Path Parser', function () {
+  it('parse simple paths', function () {
+    assertPath('', [])
+    assertPath(' ', [])
+    assertPath('a', ['a'])
+    assertPath('a.b', ['a', 'b'])
+    assertPath('a. b', ['a', 'b'])
+    assertPath('a .b', ['a', 'b'])
+    assertPath('a . b', ['a', 'b'])
+    assertPath(' a . b ', ['a', 'b'])
+    assertPath('a[0]', ['a', '0'])
+    assertPath('a [0]', ['a', '0'])
+    assertPath('a[0][1]', ['a', '0', '1'])
+    assertPath('a [ 0 ] [ 1 ] ', ['a', '0', '1'])
+    assertPath('[1234567890] ', ['1234567890'])
+    assertPath(' [1234567890] ', ['1234567890'])
+    assertPath('opt0', ['opt0'])
+    assertPath('$foo.$bar._baz', ['$foo', '$bar', '_baz'])
+    assertPath('foo["baz"]', ['foo', 'baz'])
+  })
+
+  it('parse dynamic paths', function () {
+    assertPath('foo["b\\"az"]', ['foo', '*"b\\"az"'])
+    assertPath("foo['b\\'az']", ['foo', "*'b\\'az'"])
+    assertPath('a[b][c]', ['a', '*b', '*c'])
+    assertPath('a[ b ][ c ]', ['a', '*b', '*c'])
+    assertPath('a[b.c]', ['a', '*b.c'])
+    assertPath('a[b + "c"]', ['a', '*b + "c"'])
+    assertPath('a[b[c]]', ['a', '*b[c]'])
+    assertPath('a["c" + b]', ['a', '*"c" + b'])
+  })
+
+  it('handle invalid paths', function () {
+    assertInvalidPath('.')
+    assertInvalidPath(' . ')
+    assertInvalidPath('..')
+    assertInvalidPath('a[4')
+    assertInvalidPath('a.b.')
+    assertInvalidPath('a,b')
+    assertInvalidPath('a["foo]')
+    assertInvalidPath('[0foo]')
+    assertInvalidPath('foo-bar')
+    assertInvalidPath('42')
+    assertInvalidPath('  42   ')
+    assertInvalidPath('foo["bar]')
+    assertInvalidPath("foo['bar]")
+    assertInvalidPath('a]')
+  })
+
+  it('caching', function () {
+    var path1 = Path.parsePath('a.b.c')
+    var path2 = Path.parsePath('a.b.c')
+    expect(path1).toBe(path2)
+  })
+
+  it('get', function () {
+    var path = 'a[\'b"b"c\'][0]'
+    var obj = {
+      a: {
+        'b"b"c': [12345]
+      }
+    }
+    expect(Path.getPath(obj, path)).toBe(12345)
+    expect(Path.getPath(obj, 'a.c')).toBeUndefined()
+  })
+
+  it('get dynamic', function () {
+    var path = 'a[b]'
+    var obj = {
+      a: {
+        key: 123
+      },
+      b: 'key'
+    }
+    expect(Path.getPath(obj, path)).toBe(123)
+  })
+
+  it('set', function () {
+    var path = 'a.b.c'
+    var obj = {
+      a: {
+        b: {
+          c: null
+        }
+      }
+    }
+    var res = Path.setPath(obj, path, 12345)
+    expect(res).toBe(true)
+    expect(obj.a.b.c).toBe(12345)
+  })
+
+  it('set non-existent', function () {
+    var target = {}
+    var res = Path.setPath(target, 'a.b.c', 123)
+    expect(res).toBe(true)
+    expect(target.a.b.c).toBe(123)
+  })
+
+  it('set dynamic non-existent', function () {
+    var target = {
+      key: 'what',
+      obj: {}
+    }
+    var res = Path.setPath(target, 'obj[key]', 123)
+    expect(res).toBe(true)
+    expect(target.obj.what).toBe(123)
+    // sub expressions
+    res = Path.setPath(target, 'obj["yo" + key]', 234)
+    expect(res).toBe(true)
+    expect(target.obj.yowhat).toBe(234)
+  })
+
+  it('set on prototype chain', function () {
+    var parent = { a: {} }
+    var target = Object.create(parent)
+    var res = Path.setPath(target, 'a.b.c', 123)
+    expect(res).toBe(true)
+    expect(_.hasOwn(target, 'a')).toBe(false)
+    expect(parent.a.b.c).toBe(123)
+  })
+
+  it('set array', function () {
+    var target = {
+      a: []
+    }
+    target.a.$set = jasmine.createSpy('Array.$set')
+    var res = Path.setPath(target, 'a[1]', 123)
+    expect(res).toBe(true)
+    expect(target.a.$set).toHaveBeenCalledWith('1', 123)
+  })
+
+  it('set invalid', function () {
+    var res = Path.setPath({}, 'ab[c]d', 123)
+    expect(res).toBe(false)
+  })
+})
diff --git a/test/unit/specs/parsers/template_spec.js b/test/unit/specs/parsers/template_spec.js
new file mode 100644
index 00000000000..1b74982853e
--- /dev/null
+++ b/test/unit/specs/parsers/template_spec.js
@@ -0,0 +1,191 @@
+var templateParser = require('src/parsers/template')
+var parse = templateParser.parseTemplate
+var testString = '<div>hello</div><p class="test">world</p>'
+
+describe('Template Parser', function () {
+  it('should return same if argument is already a fragment', function () {
+    var frag = document.createDocumentFragment()
+    var res = parse(frag)
+    expect(res).toBe(frag)
+  })
+
+  it('should parse if argument is a template string', function () {
+    var res = parse(testString)
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(2)
+    expect(res.querySelector('.test').textContent).toBe('world')
+  })
+
+  it('should work if the template string doesn\'t contain tags', function () {
+    var res = parse('hello!')
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(1)
+    expect(res.firstChild.nodeType).toBe(3) // Text node
+  })
+
+  it('should work if the template string doesn\'t contain tags but contains comments', function () {
+    var res = parse('<!-- yo -->hello<!-- yo -->')
+    expect(res.childNodes.length).toBe(1)
+    expect(res.firstChild.nodeType).toBe(3) // text node
+    expect(res.firstChild.nodeValue).toBe('hello')
+  })
+
+  it('should handle string that contains html entities', function () {
+    var res = parse('foo&lt;bar')
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(1)
+    expect(res.firstChild.nodeValue).toBe('foo<bar')
+    // #1330
+    res = parse('hello &#x2F; hello')
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(1)
+    expect(res.firstChild.nodeValue).toBe('hello / hello')
+    // #2021
+    res = parse('&#xe604;')
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(1)
+    expect(res.firstChild.nodeValue).toBe('')
+  })
+
+  it('should parse innerHTML if argument is a template node', function () {
+    var templateNode = document.createElement('template')
+    templateNode.innerHTML = testString
+    var res = parse(templateNode)
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(2)
+    expect(res.querySelector('.test').textContent).toBe('world')
+  })
+
+  it('should parse textContent if argument is a script node', function () {
+    var node = document.createElement('script')
+    node.textContent = testString
+    var res = parse(node)
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(2)
+    expect(res.querySelector('.test').textContent).toBe('world')
+  })
+
+  it('should parse innerHTML if argument is a normal node', function () {
+    var node = document.createElement('div')
+    node.innerHTML = testString
+    var res = parse(node)
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(2)
+    expect(res.querySelector('.test').textContent).toBe('world')
+  })
+
+  it('should retrieve and parse if argument is an id selector', function () {
+    var node = document.createElement('script')
+    node.setAttribute('id', 'template-test')
+    node.setAttribute('type', 'x/template')
+    node.textContent = testString
+    document.head.appendChild(node)
+    var res = parse('#template-test')
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(2)
+    expect(res.querySelector('.test').textContent).toBe('world')
+    document.head.removeChild(node)
+  })
+
+  it('should work for table elements', function () {
+    var res = parse('<td>hello</td>')
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(1)
+    expect(res.firstChild.tagName).toBe('TD')
+    expect(res.firstChild.textContent).toBe('hello')
+  })
+
+  it('should work for option elements', function () {
+    var res = parse('<option>hello</option>')
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(1)
+    expect(res.firstChild.tagName).toBe('OPTION')
+    expect(res.firstChild.textContent).toBe('hello')
+  })
+
+  it('should work for svg elements', function () {
+    var res = parse('<circle></circle>')
+    expect(res.nodeType).toBe(11)
+    expect(res.childNodes.length).toBe(1)
+    // SVG tagNames should be lowercase because they are XML nodes not HTML
+    expect(res.firstChild.tagName).toBe('circle')
+    expect(res.firstChild.namespaceURI).toBe('http://www.w3.org/2000/svg')
+  })
+
+  it('should cache template strings', function () {
+    var res1 = parse(testString)
+    var res2 = parse(testString)
+    expect(res1).toBe(res2)
+  })
+
+  it('should clone', function () {
+    var res1 = parse(testString, true)
+    var res2 = parse(testString, true)
+    expect(res1).not.toBe(res2)
+  })
+
+  it('should cache id selectors', function () {
+    var node = document.createElement('script')
+    node.setAttribute('id', 'template-test')
+    node.setAttribute('type', 'x/template')
+    node.textContent = '<div>never seen before content</div>'
+    document.head.appendChild(node)
+    var res1 = parse('#template-test')
+    var res2 = parse('#template-test')
+    expect(res1).toBe(res2)
+    document.head.removeChild(node)
+  })
+
+  it('should be able to not use id selectors', function () {
+    var res = parse('#hi', false, true)
+    expect(res.nodeType).toBe(11)
+    expect(res.firstChild.nodeValue).toBe('#hi')
+  })
+
+  it('should deal with Safari template clone bug', function () {
+    var a = document.createElement('div')
+    a.innerHTML = '<template>1</template>'
+    var c = templateParser.cloneNode(a)
+    expect(c.firstChild.innerHTML).toBe('1')
+  })
+
+  it('should deal with Safari template clone bug even when nested', function () {
+    var a = document.createElement('div')
+    a.innerHTML = '<template><div>1</div><template>2</template></template>'
+    var c = templateParser.cloneNode(a)
+    expect(c.firstChild.innerHTML).toBe('<div>1</div><template>2</template>')
+  })
+
+  it('should deal with IE textarea clone bug', function () {
+    var t = document.createElement('textarea')
+    t.placeholder = 't'
+    var c = templateParser.cloneNode(t)
+    expect(c.value).toBe('')
+  })
+
+  it('should trim empty text nodes and comments', function () {
+    // string
+    var res = parse('    <p>test</p>    ')
+    expect(res.childNodes.length).toBe(1)
+    expect(res.firstChild.tagName).toBe('P')
+    // nodes
+    var el = document.createElement('div')
+    el.innerHTML = '<template>    <p>test</p>    </template>'
+    res = parse(el.children[0])
+    expect(res.childNodes.length).toBe(1)
+    expect(res.firstChild.tagName).toBe('P')
+    // comments
+    res = parse('  <!-- yo -->  <p>test</p>  <!-- yo -->  ')
+    expect(res.childNodes.length).toBe(1)
+    expect(res.firstChild.tagName).toBe('P')
+  })
+
+  it('should reuse fragment from cache for the same string template', function () {
+    var stringTemplate = '    <p>test</p>    '
+    // When parsing a template, adds the created fragment to a cache
+    var res = parse(stringTemplate)
+
+    var newRes = parse(stringTemplate)
+    expect(newRes).toBe(res)
+  })
+})
diff --git a/test/unit/specs/parsers/text_spec.js b/test/unit/specs/parsers/text_spec.js
new file mode 100644
index 00000000000..c183539ea04
--- /dev/null
+++ b/test/unit/specs/parsers/text_spec.js
@@ -0,0 +1,136 @@
+var textParser = require('src/parsers/text')
+var dirParser = require('src/parsers/directive')
+var config = require('src/config')
+
+var testCases = [
+  {
+    // no tags
+    text: 'foo',
+    expected: null
+  },
+  {
+    // basic
+    text: 'a {{ a }} c',
+    expected: [
+      { value: 'a ' },
+      { tag: true, value: 'a', html: false, oneTime: false },
+      { value: ' c' }
+    ]
+  },
+  {
+    // html
+    text: '{{ text }} and {{{ html }}}',
+    expected: [
+      { tag: true, value: 'text', html: false, oneTime: false },
+      { value: ' and ' },
+      { tag: true, value: 'html', html: true, oneTime: false }
+    ]
+  },
+  {
+    // one time
+    text: '{{* text }} and {{{* html }}}',
+    expected: [
+      { tag: true, value: 'text', html: false, oneTime: true },
+      { value: ' and ' },
+      { tag: true, value: 'html', html: true, oneTime: true }
+    ]
+  },
+  {
+    text: '[{{abc}}]',
+    expected: [
+      { value: '[' },
+      { tag: true, value: 'abc', html: false, oneTime: false },
+      { value: ']' }
+    ]
+  },
+  // multiline
+  {
+    text: '{{\n  value  \n}}',
+    expected: [
+      { tag: true, value: 'value', html: false, oneTime: false }
+    ]
+  },
+  // multiline HTML
+  {
+    text: '{{{\n code \n}}}',
+    expected: [
+      { tag: true, value: 'code', html: true, oneTime: false }
+    ]
+  },
+  // new lines preserved outside of tags
+  {
+    text: 'hello\n{{value}}\nworld',
+    expected: [
+        { value: 'hello\n' },
+        { tag: true, value: 'value', html: false, oneTime: false },
+        { value: '\nworld' }
+    ]
+  }
+]
+
+function assertParse (test) {
+  var res = textParser.parseText(test.text)
+  var exp = test.expected
+  if (!Array.isArray(exp)) {
+    expect(res).toBe(exp)
+  } else {
+    expect(res.length).toBe(exp.length)
+    res.forEach(function (r, i) {
+      var e = exp[i]
+      for (var key in e) {
+        expect(e[key]).toEqual(r[key])
+      }
+    })
+  }
+}
+
+describe('Text Parser', function () {
+  it('parse', function () {
+    testCases.forEach(assertParse)
+  })
+
+  it('cache', function () {
+    var res1 = textParser.parseText('{{a}}')
+    var res2 = textParser.parseText('{{a}}')
+    expect(res1).toBe(res2)
+  })
+
+  it('custom delimiters', function () {
+    config.delimiters = ['[%', '%]']
+    config.unsafeDelimiters = ['{!!', '!!}']
+    assertParse({
+      text: '[%* text %] and {!! html !!}',
+      expected: [
+        { tag: true, value: 'text', html: false, oneTime: true },
+        { value: ' and ' },
+        { tag: true, value: 'html', html: true, oneTime: false }
+      ]
+    })
+    config.delimiters = ['{{', '}}']
+    config.unsafeDelimiters = ['{{{', '}}}']
+  })
+
+  it('tokens to expression', function () {
+    var tokens = textParser.parseText('view-{{test + 1}}-test-{{ok + "|"}}')
+    var exp = textParser.tokensToExp(tokens)
+    expect(exp).toBe('"view-"+(test + 1)+"-test-"+(ok + "|")')
+  })
+
+  it('tokens to expression, single expression', function () {
+    var tokens = textParser.parseText('{{test}}')
+    var exp = textParser.tokensToExp(tokens)
+    // should not have parens so it can be treated as a
+    // simple path by the expression parser
+    expect(exp).toBe('test')
+  })
+
+  it('tokens to expression with filters, multiple expressions', function () {
+    var tokens = textParser.parseText('a {{b | c d | f}} e')
+    var exp = textParser.tokensToExp(tokens)
+    var filters = dirParser.parseDirective('b | c d | f').filters
+    expect(exp).toBe(
+      '"a "+this._applyFilters(b,null,' +
+        JSON.stringify(filters) +
+      ',false)+" e"')
+  })
+})
diff --git a/test/unit/specs/transition/transition_spec.js b/test/unit/specs/transition/transition_spec.js
new file mode 100644
index 00000000000..5025c6efdbd
--- /dev/null
+++ b/test/unit/specs/transition/transition_spec.js
@@ -0,0 +1,581 @@
+var Vue = require('src')
+var _ = require('src/util')
+var transition = require('src/transition')
+var Transition = require('src/transition/transition')
+
+if (!_.isIE9) {
+  describe('Transition', function () {
+    // insert a test css
+    function insertCSS (text) {
+      var cssEl = document.createElement('style')
+      cssEl.textContent = text
+      document.head.appendChild(cssEl)
+    }
+
+    var duration = 100
+    insertCSS(
+      '.test {\
+        transition: opacity ' + duration + 'ms ease;\
+        -webkit-transition: opacity ' + duration + 'ms ease;}'
+    )
+    insertCSS('.test-enter, .test-leave { opacity: 0; }')
+    insertCSS(
+      '.test-anim-enter {\
+        animation: test-enter ' + duration + 'ms;\
+        -webkit-animation: test-enter ' + duration + 'ms;}\
+      .test-anim-leave {\
+        animation: test-leave ' + duration + 'ms;\
+        -webkit-animation: test-leave ' + duration + 'ms;}\
+      @keyframes test-enter {\
+        from { opacity: 0 }\
+        to { opacity: 1 }}\
+      @-webkit-keyframes test-enter {\
+        from { opacity: 0 }\
+        to { opacity: 1 }}\
+      @keyframes test-leave {\
+        from { opacity: 1 }\
+        to { opacity: 0 }}\
+      @-webkit-keyframes test-leave {\
+        from { opacity: 1 }\
+        to { opacity: 0 }}'
+    )
+
+    describe('Wrapper methods', function () {
+      var spy, el, target, parent, vm
+      beforeEach(function () {
+        el = document.createElement('div')
+        target = document.createElement('div')
+        parent = document.createElement('div')
+        parent.appendChild(target)
+        spy = jasmine.createSpy('transition skip')
+        vm = new Vue()
+        spyOn(transition, 'applyTransition')
+      })
+
+      it('append', function () {
+        transition.appendWithTransition(el, parent, vm, spy)
+        expect(parent.lastChild).toBe(el)
+        expect(spy).toHaveBeenCalled()
+      })
+
+      it('before', function () {
+        transition.beforeWithTransition(el, target, vm, spy)
+        expect(parent.firstChild).toBe(el)
+        expect(el.nextSibling).toBe(target)
+        expect(spy).toHaveBeenCalled()
+      })
+
+      it('remove', function () {
+        transition.removeWithTransition(target, vm, spy)
+        expect(parent.childNodes.length).toBe(0)
+        expect(spy).toHaveBeenCalled()
+      })
+    })
+
+    describe('Skipping', function () {
+      var el, vm, op, cb
+      beforeEach(function () {
+        el = document.createElement('div')
+        el.textContent = 'hello'
+        op = jasmine.createSpy('transition skip op')
+        cb = jasmine.createSpy('transition skip cb')
+        vm = new Vue()
+      })
+
+      it('skip el with no transition data', function () {
+        transition.applyTransition(el, 1, op, vm, cb)
+        expect(op).toHaveBeenCalled()
+        expect(cb).toHaveBeenCalled()
+      })
+
+      it('skip vm still being compiled', function () {
+        el.__v_trans = new Transition(el, 'test', null, vm)
+        transition.applyTransition(el, 1, op, vm, cb)
+        expect(op).toHaveBeenCalled()
+        expect(cb).toHaveBeenCalled()
+      })
+
+      it('skip vm with parent still being compiled', function () {
+        el.__v_trans = new Transition(el, 'test', null, vm)
+        var child = new Vue({
+          el: el,
+          parent: vm
+        })
+        expect(child._isCompiled).toBe(true)
+        transition.applyTransition(el, 1, op, child, cb)
+        expect(op).toHaveBeenCalled()
+        expect(cb).toHaveBeenCalled()
+      })
+
+      it('skip when css transition is not supported', function () {
+        var e = _.transitionEndEvent
+        _.transitionEndEvent = null
+        el.__v_trans = new Transition(el, 'test', null, vm)
+        vm.$mount(el)
+        transition.applyTransition(el, 1, op, vm, cb)
+        expect(op).toHaveBeenCalled()
+        expect(cb).toHaveBeenCalled()
+        _.transitionEndEvent = e
+      })
+    })
+
+    describe('CSS transitions', function () {
+      var vm, el, op, cb, hooks
+      beforeEach(function () {
+        el = document.createElement('div')
+        el.textContent = 'hello'
+        vm = new Vue({ el: el })
+        op = jasmine.createSpy('css op')
+        cb = jasmine.createSpy('css cb')
+        document.body.appendChild(el)
+        hooks = {
+          beforeEnter: jasmine.createSpy('beforeEnter'),
+          enter: jasmine.createSpy('enter'),
+          afterEnter: jasmine.createSpy('afterEnter'),
+          beforeLeave: jasmine.createSpy('beforeLeave'),
+          leave: jasmine.createSpy('leave'),
+          afterLeave: jasmine.createSpy('afterLeave')
+        }
+        // !IMPORTANT!
+        // this ensures we force a layout for every test.
+        /* eslint-disable no-unused-vars */
+        var f = document.body.offsetHeight
+        /* eslint-enable no-unused-vars */
+      })
+
+      afterEach(function () {
+        document.body.removeChild(el)
+      })
+
+      it('skip on 0s duration (execute right at next frame)', function (done) {
+        el.__v_trans = new Transition(el, 'test', hooks, vm)
+        el.style.transition =
+        el.style.WebkitTransition = 'opacity 0s ease'
+        transition.applyTransition(el, 1, op, vm, cb)
+        expect(hooks.beforeEnter).toHaveBeenCalled()
+        expect(hooks.enter).toHaveBeenCalled()
+        _.nextTick(function () {
+          expect(op).toHaveBeenCalled()
+          expect(cb).toHaveBeenCalled()
+          expect(hooks.afterEnter).toHaveBeenCalled()
+          expect(el.classList.contains('test-enter')).toBe(false)
+          transition.applyTransition(el, -1, op, vm, cb)
+          expect(hooks.beforeLeave).toHaveBeenCalled()
+          expect(hooks.leave).toHaveBeenCalled()
+          _.nextTick(function () {
+            expect(op.calls.count()).toBe(2)
+            expect(cb.calls.count()).toBe(2)
+            expect(hooks.afterLeave).toHaveBeenCalled()
+            expect(el.classList.contains('test-leave')).toBe(false)
+            done()
+          })
+        })
+      })
+
+      it('skip when no transition available', function (done) {
+        el.__v_trans = new Transition(el, 'test-no-trans', hooks, vm)
+        transition.applyTransition(el, 1, op, vm, cb)
+        expect(hooks.beforeEnter).toHaveBeenCalled()
+        expect(hooks.enter).toHaveBeenCalled()
+        _.nextTick(function () {
+          expect(op).toHaveBeenCalled()
+          expect(cb).toHaveBeenCalled()
+          expect(hooks.afterEnter).toHaveBeenCalled()
+          expect(el.classList.contains('test-no-trans-enter')).toBe(false)
+          // wait until transition.justEntered flag is off
+          setTimeout(function () {
+            transition.applyTransition(el, -1, op, vm, cb)
+            expect(hooks.beforeLeave).toHaveBeenCalled()
+            expect(hooks.leave).toHaveBeenCalled()
+            _.nextTick(function () {
+              expect(op.calls.count()).toBe(2)
+              expect(cb.calls.count()).toBe(2)
+              expect(hooks.afterLeave).toHaveBeenCalled()
+              expect(el.classList.contains('test-no-trans-leave')).toBe(false)
+              done()
+            })
+          }, 50)
+        })
+      })
+
+      it('transition enter', function (done) {
+        document.body.removeChild(el)
+        el.__v_trans = new Transition(el, 'test', hooks, vm)
+        // inline style
+        el.style.transition =
+        el.style.WebkitTransition = 'opacity ' + duration + 'ms ease'
+        transition.applyTransition(el, 1, function () {
+          document.body.appendChild(el)
+          op()
+        }, vm, cb)
+        expect(hooks.beforeEnter).toHaveBeenCalled()
+        expect(hooks.enter).toHaveBeenCalled()
+        expect(op).toHaveBeenCalled()
+        expect(cb).not.toHaveBeenCalled()
+        _.nextTick(function () {
+          expect(el.classList.contains('test-enter')).toBe(false)
+          expect(hooks.afterEnter).not.toHaveBeenCalled()
+          _.on(el, _.transitionEndEvent, function () {
+            expect(cb).toHaveBeenCalled()
+            expect(hooks.afterEnter).toHaveBeenCalled()
+            done()
+          })
+        })
+      })
+
+      it('transition enter for svg', function (done) {
+        el.innerHTML = '<svg><circle cx="0" cy="0" r="10"></circle></svg>'
+        var svg = el.querySelector('svg')
+        var circle = el.querySelector('circle')
+        svg.removeChild(circle)
+        circle.__v_trans = new Transition(circle, 'test', hooks, vm)
+        // inline style
+        circle.style.transition =
+        circle.style.WebkitTransition = 'opacity ' + duration + 'ms ease'
+        transition.applyTransition(circle, 1, function () {
+          svg.appendChild(circle)
+          op()
+        }, vm, cb)
+        expect(hooks.beforeEnter).toHaveBeenCalled()
+        expect(hooks.enter).toHaveBeenCalled()
+        expect(op).toHaveBeenCalled()
+        expect(cb).not.toHaveBeenCalled()
+        _.nextTick(function () {
+          expect(circle.getAttribute('class').indexOf('test-enter') > -1).toBe(false)
+          expect(hooks.afterEnter).not.toHaveBeenCalled()
+          _.on(circle, _.transitionEndEvent, function () {
+            expect(cb).toHaveBeenCalled()
+            expect(hooks.afterEnter).toHaveBeenCalled()
+            done()
+          })
+        })
+      })
+
+      it('transition leave', function (done) {
+        el.__v_trans = new Transition(el, 'test', hooks, vm)
+        // cascaded class style
+        el.classList.add('test')
+        // force a layout here so the transition can be triggered
+        /* eslint-disable no-unused-vars */
+        var f = el.offsetHeight
+        /* eslint-enable no-unused-vars */
+        transition.applyTransition(el, -1, op, vm, cb)
+        expect(hooks.beforeLeave).toHaveBeenCalled()
+        expect(hooks.leave).toHaveBeenCalled()
+        _.nextTick(function () {
+          expect(op).not.toHaveBeenCalled()
+          expect(cb).not.toHaveBeenCalled()
+          expect(hooks.afterLeave).not.toHaveBeenCalled()
+          expect(el.classList.contains('test-leave')).toBe(true)
+          _.on(el, _.transitionEndEvent, function () {
+            expect(op).toHaveBeenCalled()
+            expect(cb).toHaveBeenCalled()
+            expect(el.classList.contains('test-leave')).toBe(false)
+            expect(hooks.afterLeave).toHaveBeenCalled()
+            done()
+          })
+        })
+      })
+
+      it('transition leave for svg', function (done) {
+        el.innerHTML = '<svg><circle cx="0" cy="0" r="10" class="test"></circle></svg>'
+        var circle = el.querySelector('circle')
+        circle.__v_trans = new Transition(circle, 'test', hooks, vm)
+        // force a layout here so the transition can be triggered
+        /* eslint-disable no-unused-vars */
+        var f = el.offsetHeight
+        /* eslint-enable no-unused-vars */
+        transition.applyTransition(circle, -1, op, vm, cb)
+        expect(hooks.beforeLeave).toHaveBeenCalled()
+        expect(hooks.leave).toHaveBeenCalled()
+        _.nextTick(function () {
+          expect(op).not.toHaveBeenCalled()
+          expect(cb).not.toHaveBeenCalled()
+          expect(hooks.afterLeave).not.toHaveBeenCalled()
+          expect(circle.getAttribute('class').indexOf('test-leave') > -1).toBe(true)
+          _.on(circle, _.transitionEndEvent, function () {
+            expect(op).toHaveBeenCalled()
+            expect(cb).toHaveBeenCalled()
+            expect(circle.getAttribute('class').indexOf('test-leave') > -1).toBe(false)
+            expect(hooks.afterLeave).toHaveBeenCalled()
+            done()
+          })
+        })
+      })
+
+      it('animation enter', function (done) {
+        document.body.removeChild(el)
+        el.__v_trans = new Transition(el, 'test-anim', hooks, vm)
+        transition.applyTransition(el, 1, function () {
+          document.body.appendChild(el)
+          op()
+        }, vm, cb)
+        expect(hooks.beforeEnter).toHaveBeenCalled()
+        expect(hooks.enter).toHaveBeenCalled()
+        _.nextTick(function () {
+          expect(op).toHaveBeenCalled()
+          expect(cb).not.toHaveBeenCalled()
+          expect(el.classList.contains('test-anim-enter')).toBe(true)
+          expect(hooks.afterEnter).not.toHaveBeenCalled()
+          _.on(el, _.animationEndEvent, function () {
+            expect(el.classList.contains('test-anim-enter')).toBe(false)
+            expect(cb).toHaveBeenCalled()
+            expect(hooks.afterEnter).toHaveBeenCalled()
+            done()
+          })
+        })
+      })
+
+      it('animation leave', function (done) {
+        el.__v_trans = new Transition(el, 'test-anim', hooks, vm)
+        transition.applyTransition(el, -1, op, vm, cb)
+        expect(hooks.beforeLeave).toHaveBeenCalled()
+        expect(hooks.leave).toHaveBeenCalled()
+        _.nextTick(function () {
+          expect(op).not.toHaveBeenCalled()
+          expect(cb).not.toHaveBeenCalled()
+          expect(el.classList.contains('test-anim-leave')).toBe(true)
+          expect(hooks.afterLeave).not.toHaveBeenCalled()
+          _.on(el, _.animationEndEvent, function () {
+            expect(op).toHaveBeenCalled()
+            expect(cb).toHaveBeenCalled()
+            expect(el.classList.contains('test-anim-leave')).toBe(false)
+            expect(hooks.afterLeave).toHaveBeenCalled()
+            done()
+          })
+        })
+      })
+
+      it('css + js hook with callback', function (done) {
+        document.body.removeChild(el)
+        el.classList.add('test')
+
+        // enter hook that expects a second argument
+        // indicates the user wants to control when the
+        // transition ends.
+        var enterCalled = false
+        hooks.enter = function (el, enterDone) {
+          enterCalled = true
+          setTimeout(function () {
+            enterDone()
+            testDone()
+          }, duration * 1.5)
+        }
+
+        el.__v_trans = new Transition(el, 'test', hooks, vm)
+        transition.applyTransition(el, 1, function () {
+          document.body.appendChild(el)
+          op()
+        }, vm, cb)
+        expect(hooks.beforeEnter).toHaveBeenCalled()
+        expect(op).toHaveBeenCalled()
+        expect(cb).not.toHaveBeenCalled()
+        expect(enterCalled).toBe(true)
+        _.nextTick(function () {
+          expect(el.classList.contains('test-enter')).toBe(false)
+          expect(hooks.afterEnter).not.toHaveBeenCalled()
+          _.on(el, _.transitionEndEvent, function () {
+            // should wait until js callback is called!
+            expect(cb).not.toHaveBeenCalled()
+            expect(hooks.afterEnter).not.toHaveBeenCalled()
+          })
+        })
+
+        // this is called by the enter hook
+        function testDone () {
+          expect(cb).toHaveBeenCalled()
+          expect(hooks.afterEnter).toHaveBeenCalled()
+          done()
+        }
+      })
+
+      it('css + js hook with callback before transitionend', function (done) {
+        document.body.removeChild(el)
+        el.classList.add('test')
+
+        // enter hook that expects a second argument
+        // indicates the user wants to control when the
+        // transition ends.
+        var enterCalled = false
+        hooks.enter = function (el, enterDone) {
+          enterCalled = true
+          setTimeout(function () {
+            enterDone()
+            testDone()
+          }, duration / 2)
+        }
+
+        el.__v_trans = new Transition(el, 'test', hooks, vm)
+        transition.applyTransition(el, 1, function () {
+          document.body.appendChild(el)
+          op()
+        }, vm, cb)
+        expect(hooks.beforeEnter).toHaveBeenCalled()
+        expect(op).toHaveBeenCalled()
+        expect(cb).not.toHaveBeenCalled()
+        expect(enterCalled).toBe(true)
+        _.nextTick(function () {
+          expect(el.classList.contains('test-enter')).toBe(false)
+          expect(hooks.afterEnter).not.toHaveBeenCalled()
+          _.on(el, _.transitionEndEvent, function () {
+            // callback should have been called, but only once, by the js callback
+            expect(cb).toHaveBeenCalled()
+            expect(cb.calls.count()).toBe(1)
+            expect(hooks.afterEnter).toHaveBeenCalled()
+            done()
+          })
+        })
+
+        // this is called by the enter hook
+        function testDone () {
+          expect(cb).toHaveBeenCalled()
+          expect(hooks.afterEnter).toHaveBeenCalled()
+        }
+      })
+
+      it('clean up unfinished css callback', function (done) {
+        el.__v_trans = new Transition(el, 'test', null, vm)
+        el.classList.add('test')
+        transition.applyTransition(el, -1, function () {
+          document.body.removeChild(el)
+        }, vm, cb)
+        // cancel early
+        _.nextTick(function () {
+          expect(el.__v_trans.pendingCssCb).toBeTruthy()
+          expect(el.classList.contains('test-leave')).toBe(true)
+          transition.applyTransition(el, 1, function () {
+            document.body.appendChild(el)
+          }, vm)
+          expect(cb).not.toHaveBeenCalled()
+          expect(el.classList.contains('test-leave')).toBe(false)
+          expect(el.__v_trans.pendingCssCb).toBeNull()
+          // IMPORTANT
+          // Let the queue flush finish before enter the next
+          // test. Don't remove the nextTick.
+          _.nextTick(done)
+        })
+      })
+
+      it('cache transition sniff results', function (done) {
+        el.__v_trans = new Transition(el, 'test', null, vm)
+        el.classList.add('test')
+        transition.applyTransition(el, 1, op, vm)
+        _.nextTick(function () {
+          expect(el.__v_trans.typeCache['test-enter']).not.toBeUndefined()
+          // for some reason window.getComputedStyle cannot be spied on in
+          // phantomjs after the refactor...
+          var calls = 0
+          Object.defineProperty(el.__v_trans.typeCache, 'test-enter', {
+            get: function () {
+              calls++
+              return 1
+            }
+          })
+          transition.applyTransition(el, 1, op, vm)
+          _.nextTick(function () {
+            expect(calls).toBe(1)
+            done()
+          })
+        })
+      })
+    })
+
+    describe('JavaScript only transitions', function () {
+      var el, vm, op, cb, hooks
+      beforeEach(function () {
+        hooks = {}
+        el = document.createElement('div')
+        el.textContent = 'hello'
+        document.body.appendChild(el)
+        op = jasmine.createSpy('js transition op')
+        cb = jasmine.createSpy('js transition cb')
+        vm = new Vue({ el: el })
+      })
+
+      afterEach(function () {
+        document.body.removeChild(el)
+      })
+
+      it('beforeEnter', function () {
+        var spy = jasmine.createSpy('js transition beforeEnter')
+        hooks.beforeEnter = function (el) {
+          spy(this, el)
+        }
+        el.__v_trans = new Transition(el, 'test', hooks, vm)
+        transition.applyTransition(el, 1, op, vm, cb)
+        expect(spy).toHaveBeenCalledWith(vm, el)
+      })
+
+      it('enter', function () {
+        var spy = jasmine.createSpy('js enter')
+        hooks.enter = function (e, done) {
+          expect(e).toBe(el)
+          expect(op).toHaveBeenCalled()
+          done()
+          expect(cb).toHaveBeenCalled()
+          spy(this)
+        }
+        el.__v_trans = new Transition(el, 'test', hooks, vm)
+        transition.applyTransition(el, 1, op, vm, cb)
+        expect(spy).toHaveBeenCalledWith(vm)
+      })
+
+      it('leave', function () {
+        var spy = jasmine.createSpy('js leave')
+        hooks.leave = function (e, done) {
+          expect(e).toBe(el)
+          done()
+          expect(op).toHaveBeenCalled()
+          expect(cb).toHaveBeenCalled()
+          spy(this)
+        }
+        el.__v_trans = new Transition(el, 'test', hooks, vm)
+        transition.applyTransition(el, -1, op, vm, cb)
+        expect(spy).toHaveBeenCalledWith(vm)
+      })
+
+      it('no def', function (done) {
+        el.__v_trans = new Transition(el, 'test', null, vm)
+        transition.applyTransition(el, 1, op, vm, cb)
+        _.nextTick(function () {
+          expect(op).toHaveBeenCalled()
+          expect(cb).toHaveBeenCalled()
+          transition.applyTransition(el, -1, op, vm, cb)
+          _.nextTick(function () {
+            expect(op.calls.count()).toBe(2)
+            expect(cb.calls.count()).toBe(2)
+            done()
+          })
+        })
+      })
+
+      it('cancel hook', function (done) {
+        var cleanupSpy = jasmine.createSpy('js cleanup')
+        var leaveSpy = jasmine.createSpy('js leave')
+        var timeout
+        hooks.enter = function (el, done) {
+          timeout = setTimeout(done, duration / 2)
+        }
+        hooks.enterCancelled = function () {
+          clearTimeout(timeout)
+          cleanupSpy()
+        }
+        hooks.leave = function (el, done) {
+          expect(cleanupSpy).toHaveBeenCalled()
+          leaveSpy()
+          done()
+        }
+        el.__v_trans = new Transition(el, 'test', hooks, vm)
+        transition.applyTransition(el, 1, op, vm, cb)
+        setTimeout(function () {
+          transition.applyTransition(el, -1, op, vm)
+          expect(leaveSpy).toHaveBeenCalled()
+          setTimeout(function () {
+            expect(cb).not.toHaveBeenCalled()
+            done()
+          }, duration / 2)
+        }, duration / 4)
+      })
+    })
+  })
+}
diff --git a/test/unit/specs/util/component_spec.js b/test/unit/specs/util/component_spec.js
new file mode 100644
index 00000000000..7faaea21245
--- /dev/null
+++ b/test/unit/specs/util/component_spec.js
@@ -0,0 +1,57 @@
+var _ = require('src/util')
+
+describe('Util - component', function () {
+  it('checkComponentAttr', function () {
+    var el = document.createElement('component')
+    var mockOptions = { components: {
+      foo: {}
+    }}
+
+    // <component> with no is attr
+    var res = _.checkComponentAttr(el, mockOptions)
+    expect(res).toBeUndefined()
+
+    // static <component is="...">
+    el.setAttribute('is', 'foo')
+    res = _.checkComponentAttr(el, mockOptions)
+    expect(res.id).toBe('foo')
+    expect(res.dynamic).toBeFalsy()
+
+    // <component :is="...">
+    el.setAttribute(':is', 'foo')
+    res = _.checkComponentAttr(el, mockOptions)
+    expect(res.id).toBe('foo')
+    expect(res.dynamic).toBe(true)
+
+    // <test is="...">
+    el = document.createElement('test')
+    el.setAttribute('is', 'foo')
+    res = _.checkComponentAttr(el, mockOptions)
+    expect(res.id).toBe('foo')
+    expect(res.dynamic).toBeUndefined()
+
+    // <test :is="...">
+    el = document.createElement('test')
+    el.setAttribute(':is', 'foo')
+    res = _.checkComponentAttr(el, mockOptions)
+    expect(res.id).toBe('foo')
+    expect(res.dynamic).toBe(true)
+
+    // custom element, not defined
+    el = document.createElement('test')
+    res = _.checkComponentAttr(el, mockOptions)
+    expect(res).toBeUndefined()
+
+    // custom element, defined
+    el = document.createElement('foo')
+    res = _.checkComponentAttr(el, mockOptions)
+    expect(res.id).toBe('foo')
+
+    // is on undefined custom element
+    // should be preserved in case it is a native custom element usage
+    el = document.createElement('test2')
+    el.setAttribute('is', 'bar')
+    res = _.checkComponentAttr(el, mockOptions)
+    expect(res).toBeUndefined()
+  })
+})
diff --git a/test/unit/specs/util/debug_spec.js b/test/unit/specs/util/debug_spec.js
new file mode 100644
index 00000000000..1f12d5fa599
--- /dev/null
+++ b/test/unit/specs/util/debug_spec.js
@@ -0,0 +1,35 @@
+var _ = require('src/util')
+var Vue = require('src')
+var config = require('src/config')
+var warnPrefix = '[Vue warn]: '
+
+if (typeof console !== 'undefined') {
+  describe('Util - Debug', function () {
+    beforeEach(function () {
+      spyOn(console, 'error')
+    })
+
+    it('warn when silent is false', function () {
+      config.silent = false
+      _.warn.and.callThrough()
+      _.warn('oops')
+      expect(console.error).toHaveBeenCalledWith(warnPrefix + 'oops')
+    })
+
+    it('format component name', function () {
+      config.silent = false
+      _.warn.and.callThrough()
+      _.warn('oops', new Vue({ name: 'foo' }))
+      expect(console.error).toHaveBeenCalledWith(warnPrefix + 'oops (found in component: <foo>)')
+      _.warn('oops', { name: 'bar' })
+      expect(console.error).toHaveBeenCalledWith(warnPrefix + 'oops (found in component: <bar>)')
+    })
+
+    it('not warn when silent is ture', function () {
+      config.silent = true
+      _.warn.and.callThrough()
+      _.warn('oops')
+      expect(console.error).not.toHaveBeenCalled()
+    })
+  })
+}
diff --git a/test/unit/specs/util/dom_spec.js b/test/unit/specs/util/dom_spec.js
new file mode 100644
index 00000000000..16c2cc1f98e
--- /dev/null
+++ b/test/unit/specs/util/dom_spec.js
@@ -0,0 +1,138 @@
+var _ = require('src/util')
+
+describe('Util - DOM', function () {
+  var parent, child, target
+
+  function div () {
+    return document.createElement('div')
+  }
+
+  beforeEach(function () {
+    parent = div()
+    child = div()
+    target = div()
+    parent.appendChild(child)
+  })
+
+  it('inDoc', function () {
+    expect(_.inDoc(target)).toBe(false)
+    document.body.appendChild(target)
+    expect(_.inDoc(target)).toBe(true)
+    document.body.removeChild(target)
+    expect(_.inDoc(target)).toBe(false)
+  })
+
+  it('inDoc (iframe)', function (done) {
+    var f = document.createElement('iframe')
+    f.onload = function () {
+      f.contentWindow.document.body.appendChild(target)
+      expect(_.inDoc(target)).toBe(true)
+      document.body.removeChild(f)
+      done()
+    }
+    document.body.appendChild(f)
+    f.src = 'about:blank'
+  })
+
+  it('getAttr', function () {
+    target.setAttribute('v-test', 'ok')
+    var val = _.getAttr(target, 'v-test')
+    expect(val).toBe('ok')
+    expect(target.hasAttribute('v-test')).toBe(false)
+  })
+
+  it('before', function () {
+    _.before(target, child)
+    expect(target.parentNode).toBe(parent)
+    expect(target.nextSibling).toBe(child)
+  })
+
+  it('after', function () {
+    _.after(target, child)
+    expect(target.parentNode).toBe(parent)
+    expect(child.nextSibling).toBe(target)
+  })
+
+  it('after with sibling', function () {
+    var sibling = div()
+    parent.appendChild(sibling)
+    _.after(target, child)
+    expect(target.parentNode).toBe(parent)
+    expect(child.nextSibling).toBe(target)
+  })
+
+  it('remove', function () {
+    _.remove(child)
+    expect(child.parentNode).toBeNull()
+    expect(parent.childNodes.length).toBe(0)
+  })
+
+  it('prepend', function () {
+    _.prepend(target, parent)
+    expect(target.parentNode).toBe(parent)
+    expect(parent.firstChild).toBe(target)
+  })
+
+  it('prepend to empty node', function () {
+    parent.removeChild(child)
+    _.prepend(target, parent)
+    expect(target.parentNode).toBe(parent)
+    expect(parent.firstChild).toBe(target)
+  })
+
+  it('replace', function () {
+    _.replace(child, target)
+    expect(parent.childNodes.length).toBe(1)
+    expect(parent.firstChild).toBe(target)
+  })
+
+  it('on/off', function () {
+    // IE requires element to be in document to fire events
+    document.body.appendChild(target)
+    var spy = jasmine.createSpy()
+    _.on(target, 'click', spy)
+    var e = document.createEvent('HTMLEvents')
+    e.initEvent('click', true, true)
+    target.dispatchEvent(e)
+    expect(spy.calls.count()).toBe(1)
+    expect(spy).toHaveBeenCalledWith(e)
+    _.off(target, 'click', spy)
+    target.dispatchEvent(e)
+    expect(spy.calls.count()).toBe(1)
+    document.body.removeChild(target)
+  })
+
+  it('addClass/removeClass', function () {
+    var el = document.createElement('div')
+    el.className = 'aa bb cc'
+    _.removeClass(el, 'bb')
+    expect(el.className).toBe('aa cc')
+    _.removeClass(el, 'aa')
+    expect(el.className).toBe('cc')
+    _.addClass(el, 'bb')
+    expect(el.className).toBe('cc bb')
+    _.addClass(el, 'bb')
+    expect(el.className).toBe('cc bb')
+  })
+
+  it('addClass/removeClass for SVG/IE9', function () {
+    var el = document.createElementNS('http://www.w3.org/2000/svg', 'circle')
+    el.setAttribute('class', 'aa bb cc')
+    _.removeClass(el, 'bb')
+    expect(el.getAttribute('class')).toBe('aa cc')
+    _.removeClass(el, 'aa')
+    expect(el.getAttribute('class')).toBe('cc')
+    _.addClass(el, 'bb')
+    expect(el.getAttribute('class')).toBe('cc bb')
+    _.addClass(el, 'bb')
+    expect(el.getAttribute('class')).toBe('cc bb')
+  })
+
+  it('getOuterHTML for SVG', function () {
+    var el = document.createElementNS('http://www.w3.org/2000/svg', 'circle')
+    el.setAttribute('class', 'aa bb cc')
+    var html = _.getOuterHTML(el)
+    var re = /<circle (xmlns="http:\/\/www\.w3\.org\/2000\/svg"\s)?class="aa bb cc"(\s?\/>|><\/circle>)/
+    expect(re.test(html)).toBe(true)
+  })
+})
diff --git a/test/unit/specs/util/env_spec.js b/test/unit/specs/util/env_spec.js
new file mode 100644
index 00000000000..fb79011b50f
--- /dev/null
+++ b/test/unit/specs/util/env_spec.js
@@ -0,0 +1,16 @@
+var _ = require('src/util')
+
+describe('Util - Environment', function () {
+  describe('nextTick', function () {
+    it('should accept context', function (done) {
+      var ctx = {}
+      _.nextTick(function () {
+        this.id = 1
+      }, ctx)
+      _.nextTick(function () {
+        expect(ctx.id).toBe(1)
+        done()
+      })
+    })
+  })
+})
diff --git a/test/unit/specs/util/lang_spec.js b/test/unit/specs/util/lang_spec.js
new file mode 100644
index 00000000000..cef1a6efcb0
--- /dev/null
+++ b/test/unit/specs/util/lang_spec.js
@@ -0,0 +1,166 @@
+var _ = require('src/util')
+
+describe('Util - Language Enhancement', function () {
+  it('hasOwn', function () {
+    var obj1 = { a: 1 }
+    expect(_.hasOwn(obj1, 'a')).toBe(true)
+    var obj2 = Object.create(null)
+    obj2.a = 2
+    expect(_.hasOwn(obj2, 'a')).toBe(true)
+  })
+
+  it('isLiteral', function () {
+    expect(_.isLiteral('123')).toBe(true)
+    expect(_.isLiteral('12.3')).toBe(true)
+    expect(_.isLiteral('true')).toBe(true)
+    expect(_.isLiteral(' false ')).toBe(true)
+    expect(_.isLiteral('"foo"')).toBe(true)
+    expect(_.isLiteral(" 'foo' ")).toBe(true)
+    expect(_.isLiteral('a.b.c')).toBe(false)
+    expect(_.isLiteral('1 + 1')).toBe(false)
+  })
+
+  it('toString', function () {
+    expect(_._toString('foo')).toBe('foo')
+    expect(_._toString(1.234)).toBe('1.234')
+    expect(_._toString(null)).toBe('')
+    expect(_._toString(undefined)).toBe('')
+  })
+
+  it('toNumber', function () {
+    expect(_.toNumber('12')).toBe(12)
+    expect(_.toNumber('1e5')).toBe(1e5)
+    expect(_.toNumber('0x2F')).toBe(0x2F)
+    expect(_.toNumber(null)).toBe(null)
+    expect(_.toNumber(true)).toBe(true)
+    expect(_.toNumber('hello')).toBe('hello')
+  })
+
+  it('strip quotes', function () {
+    expect(_.stripQuotes('"123"')).toBe('123')
+    expect(_.stripQuotes("'fff'")).toBe('fff')
+    expect(_.stripQuotes("'fff")).toBe("'fff")
+  })
+
+  it('camelize', function () {
+    expect(_.camelize('abc')).toBe('abc')
+    expect(_.camelize('some-long-name')).toBe('someLongName')
+  })
+
+  it('hyphenate', function () {
+    expect(_.hyphenate('fooBar')).toBe('foo-bar')
+    expect(_.hyphenate('a1BfC')).toBe('a1-bf-c')
+    expect(_.hyphenate('already-With-Hyphen')).toBe('already-with-hyphen')
+    expect(_.hyphenate('ABigApple')).toBe('a-big-apple')
+  })
+
+  it('classify', function () {
+    expect(_.classify('abc')).toBe('Abc')
+    expect(_.classify('foo-bar')).toBe('FooBar')
+    expect(_.classify('foo_bar')).toBe('FooBar')
+    expect(_.classify('foo/bar')).toBe('FooBar')
+  })
+
+  it('bind', function () {
+    var original = function (a) {
+      return this.a + a
+    }
+    var ctx = { a: 'ctx a ' }
+    var bound = _.bind(original, ctx)
+    var res = bound('arg a')
+    expect(res).toBe('ctx a arg a')
+  })
+
+  it('toArray', function () {
+    // should make a copy of original array
+    var arr = [1, 2, 3]
+    var res = _.toArray(arr)
+    expect(Array.isArray(res)).toBe(true)
+    expect(res.toString()).toEqual('1,2,3')
+    expect(res).not.toBe(arr)
+
+    // should work on arguments
+    ;(function () {
+      var res = _.toArray(arguments)
+      expect(Array.isArray(res)).toBe(true)
+      expect(res.toString()).toEqual('1,2,3')
+    })(1, 2, 3)
+  })
+
+  it('extend', function () {
+    var from = {a: 1, b: 2}
+    var to = {}
+    var res = _.extend(to, from)
+    expect(to.a).toBe(from.a)
+    expect(to.b).toBe(from.b)
+    expect(res).toBe(to)
+  })
+
+  it('isObject', function () {
+    expect(_.isObject({})).toBe(true)
+    expect(_.isObject([])).toBe(true)
+    expect(_.isObject(null)).toBeFalsy()
+    expect(_.isObject(123)).toBeFalsy()
+    expect(_.isObject(true)).toBeFalsy()
+    expect(_.isObject('foo')).toBeFalsy()
+    expect(_.isObject(undefined)).toBeFalsy()
+    expect(_.isObject(function () {})).toBeFalsy()
+  })
+
+  it('isPlainObject', function () {
+    expect(_.isPlainObject({})).toBe(true)
+    expect(_.isPlainObject([])).toBe(false)
+    expect(_.isPlainObject(null)).toBe(false)
+    expect(_.isPlainObject(null)).toBeFalsy()
+    expect(_.isPlainObject(123)).toBeFalsy()
+    expect(_.isPlainObject(true)).toBeFalsy()
+    expect(_.isPlainObject('foo')).toBeFalsy()
+    expect(_.isPlainObject(undefined)).toBeFalsy()
+    expect(_.isPlainObject(function () {})).toBe(false)
+    expect(_.isPlainObject(window)).toBe(false)
+  })
+
+  it('isArray', function () {
+    expect(_.isArray([])).toBe(true)
+    expect(_.isArray({})).toBe(false)
+    expect(_.isArray(arguments)).toBe(false)
+  })
+
+  it('define', function () {
+    var obj = {}
+    _.def(obj, 'test', 123)
+    expect(obj.test).toBe(123)
+    var desc = Object.getOwnPropertyDescriptor(obj, 'test')
+    expect(desc.enumerable).toBe(false)
+
+    _.def(obj, 'test2', 123, true)
+    expect(obj.test2).toBe(123)
+    desc = Object.getOwnPropertyDescriptor(obj, 'test2')
+    expect(desc.enumerable).toBe(true)
+  })
+
+  it('debounce', function (done) {
+    var count = 0
+    var fn = _.debounce(function () {
+      count++
+    }, 100)
+    fn()
+    setTimeout(fn, 10)
+    setTimeout(fn, 20)
+    setTimeout(function () {
+      expect(count).toBe(0)
+    }, 30)
+    setTimeout(function () {
+      expect(count).toBe(1)
+      done()
+    }, 200)
+  })
+
+  it('looseEqual', function () {
+    expect(_.looseEqual(1, '1')).toBe(true)
+    expect(_.looseEqual(null, undefined)).toBe(true)
+    expect(_.looseEqual({a: 1}, {a: 1})).toBe(true)
+    expect(_.looseEqual({a: 1}, {a: 2})).toBe(false)
+    expect(_.looseEqual({}, [])).toBe(false)
+  })
+})
diff --git a/test/unit/specs/util/options_spec.js b/test/unit/specs/util/options_spec.js
new file mode 100644
index 00000000000..3d009636ed0
--- /dev/null
+++ b/test/unit/specs/util/options_spec.js
@@ -0,0 +1,391 @@
+var _ = require('src/util')
+var Vue = require('src')
+var merge = _.mergeOptions
+var resolveAsset = _.resolveAsset
+
+describe('Util - Option merging', function () {
+  it('default strat', function () {
+    // child undefined
+    var res = merge({replace: true}, {}).replace
+    expect(res).toBe(true)
+    // child overwrite
+    res = merge({replace: true}, {replace: false}).replace
+    expect(res).toBe(false)
+  })
+
+  it('hooks', function () {
+    var fn1 = function () {}
+    var fn2 = function () {}
+    var res
+    // parent undefined
+    res = merge({}, {created: fn1}).created
+    expect(Array.isArray(res)).toBe(true)
+    expect(res.length).toBe(1)
+    expect(res[0]).toBe(fn1)
+    // child undefined
+    res = merge({created: [fn1]}, {}).created
+    expect(Array.isArray(res)).toBe(true)
+    expect(res.length).toBe(1)
+    expect(res[0]).toBe(fn1)
+    // both defined
+    res = merge({created: [fn1]}, {created: fn2}).created
+    expect(Array.isArray(res)).toBe(true)
+    expect(res.length).toBe(2)
+    expect(res[0]).toBe(fn1)
+    expect(res[1]).toBe(fn2)
+  })
+
+  it('events', function () {
+    // no parent
+    res = merge({}, {events: 1})
+    expect(res.events).toBe(1)
+    // no child
+    res = merge({events: 1}, {})
+    expect(res.events).toBe(1)
+
+    var fn1 = function () {}
+    var fn2 = function () {}
+    var fn3 = function () {}
+    var parent = {
+      events: {
+        'fn1': [fn1, fn2],
+        'fn2': fn2
+      }
+    }
+    var child = {
+      events: {
+        'fn1': fn3,
+        'fn2': fn3,
+        'fn3': fn3
+      }
+    }
+    var res = merge(parent, child).events
+    assertRes(res.fn1, [fn1, fn2, fn3])
+    assertRes(res.fn2, [fn2, fn3])
+    assertRes(res.fn3, [fn3])
+
+    function assertRes (res, expected) {
+      expect(Array.isArray(res)).toBe(true)
+      expect(res.length).toBe(expected.length)
+      var i = expected.length
+      while (i--) {
+        expect(res[i]).toBe(expected[i])
+      }
+    }
+  })
+
+  it('normal object hashes', function () {
+    var fn1 = function () {}
+    var fn2 = function () {}
+    var res
+    // parent undefined
+    res = merge({}, {methods: {test: fn1}}).methods
+    expect(res.test).toBe(fn1)
+    // child undefined
+    res = merge({methods: {test: fn1}}, {}).methods
+    expect(res.test).toBe(fn1)
+    // both defined
+    var parent = {methods: {test: fn1}}
+    res = merge(parent, {methods: {test2: fn2}}).methods
+    expect(res.test).toBe(fn1)
+    expect(res.test2).toBe(fn2)
+  })
+
+  it('assets', function () {
+    var asset1 = {}
+    var asset2 = {}
+    var res = merge(
+      { directives: { a: asset1 }},
+      { directives: { b: asset2 }}
+    ).directives
+    expect(res.a).toBe(asset1)
+    expect(res.b).toBe(asset2)
+  })
+
+  it('props', function () {
+    var res = merge({
+      props: {
+        a: null,
+        d: null
+      }
+    }, {
+      props: {
+        a: { required: true },
+        b: Boolean,
+        c: { type: Array }
+      }
+    })
+    expect(typeof res.props.a).toBe('object')
+    expect(res.props.a.required).toBe(true)
+    expect(typeof res.props.b).toBe('object')
+    expect(res.props.b.type).toBe(Boolean)
+    expect(typeof res.props.c).toBe('object')
+    expect(res.props.c.type).toBe(Array)
+    expect(res.props.d).toBe(null)
+
+    // check array syntax
+    res = merge({
+      props: {
+        b: null
+      }
+    }, {
+      props: ['a']
+    })
+    expect(res.props.a).toBe(null)
+    expect(res.props.b).toBe(null)
+  })
+
+  it('guard components', function () {
+    var res = merge({
+      components: null
+    }, {
+      components: {
+        test: { template: 'foo' }
+      }
+    })
+    expect(typeof res.components.test).toBe('function')
+    expect(res.components.test.super).toBe(Vue)
+  })
+
+  it('guard components warn built-in elements', function () {
+    merge({
+      components: null
+    }, {
+      components: {
+        a: { template: 'foo' }
+      }
+    })
+    expect('Do not use built-in or reserved HTML elements as component id: a').toHaveBeenWarned()
+    merge({
+      components: null
+    }, {
+      components: {
+        slot: { template: 'foo' }
+      }
+    })
+    expect('Do not use built-in or reserved HTML elements as component id: slot').toHaveBeenWarned()
+  })
+
+  it('should ignore non-function el & data in class merge', function () {
+    var res = merge({}, {el: 1, data: 2})
+    expect(res.el).toBeUndefined()
+    expect(res.data).toBeUndefined()
+  })
+
+  it('class el merge', function () {
+    function fn1 () {}
+    function fn2 () {}
+    var res = merge({ el: fn1 }, { el: fn2 })
+    expect(res.el).toBe(fn2)
+  })
+
+  it('class data merge', function () {
+    function fn1 () {
+      return { a: 1, c: 4, d: { e: 1 }}
+    }
+    function fn2 () {
+      return { a: 2, b: 3, d: { f: 2 }}
+    }
+    // both present
+    var res = merge({ data: fn1 }, { data: fn2 }).data()
+    expect(res.a).toBe(2)
+    expect(res.b).toBe(3)
+    expect(res.c).toBe(4)
+    expect(res.d.e).toBe(1)
+    expect(res.d.f).toBe(2)
+    // only parent
+    res = merge({ data: fn1 }, {}).data()
+    expect(res.a).toBe(1)
+    expect(res.b).toBeUndefined()
+    expect(res.c).toBe(4)
+    expect(res.d.e).toBe(1)
+    expect(res.d.f).toBeUndefined()
+  })
+
+  it('instanace el merge', function () {
+    var vm = {} // mock vm presence
+    function fn1 () {
+      expect(this).toBe(vm)
+      return 1
+    }
+    function fn2 () {
+      expect(this).toBe(vm)
+      return 2
+    }
+    // both functions
+    var res = merge({ el: fn1 }, { el: fn2 }, vm)
+    expect(res.el).toBe(2)
+    // direct instance el
+    res = merge({ el: fn1 }, { el: 2 }, vm)
+    expect(res.el).toBe(2)
+    // no parent
+    res = merge({}, { el: 2 }, vm)
+    expect(res.el).toBe(2)
+    // no child
+    res = merge({ el: fn1 }, {}, vm)
+    expect(res.el).toBe(1)
+  })
+
+  it('instance data merge with no instance data', function () {
+    var res = merge(
+      {data: function () {
+        return { a: 1}
+      }},
+      {}, // no instance data
+      {} // mock vm presence
+    )
+    expect(res.data().a).toBe(1)
+  })
+
+  it('instance data merge with default data function', function () {
+    var vm = {} // mock vm presence
+    var res = merge(
+      // component default
+      { data: function () {
+        expect(this).toBe(vm)
+        return {
+          a: 1,
+          b: 2
+        }
+      }},
+      { data: { a: 2 }}, // instance data
+      vm
+    )
+    var data = res.data()
+    expect(data.a).toBe(2)
+    expect(data.b).toBe(2)
+  })
+
+  it('already observed instance data merge with default data', function () {
+    var observe = require('src/observer').observe
+    var instanceData = { a: 123 }
+    // observe it
+    observe(instanceData)
+    var res = merge(
+      {
+        data: function () {
+          return { b: 234 }
+        }
+      },
+      {
+        data: instanceData
+      },
+      {}
+    )
+    var data = res.data()
+    expect(data.a).toBe(123)
+    expect(data.b).toBe(234)
+    expect(Object.getOwnPropertyDescriptor(data, 'b').get).toBeTruthy()
+  })
+
+  it('extends', function () {
+    var f1 = function () {}
+    var f2 = function () {}
+    var f3 = function () {}
+    var componentA = Vue.extend({ template: 'foo', methods: { f1: f1, f2: function () {} }})
+    var componentB = { extends: componentA, methods: { f2: f2 }}
+    var componentC = { extends: componentB, template: 'bar', methods: { f3: f3 }}
+    var res = merge({}, componentC)
+    expect(res.template).toBe('bar')
+    expect(res.methods.f1).toBe(f1)
+    expect(res.methods.f2).toBe(f2)
+    expect(res.methods.f3).toBe(f3)
+  })
+
+  it('mixins', function () {
+    var a = {}
+    var b = {}
+    var c = {}
+    var d = {}
+    var f1 = function () {}
+    var f2 = function () {}
+    var f3 = function () {}
+    var f4 = function () {}
+    var mixinA = { a: 1, directives: { a: a }, created: f2 }
+    var mixinB = { b: 1, directives: { b: b }, created: f3 }
+    var mixinC = Vue.extend({ c: 1 })
+    var res = merge(
+      { a: 2, directives: { c: c }, created: [f1] },
+      { directives: { d: d }, mixins: [mixinA, mixinB, mixinC], created: f4 }
+    )
+    expect(res.a).toBe(1)
+    expect(res.b).toBe(1)
+    expect(res.c).toBe(1)
+    expect(res.directives.a).toBe(a)
+    expect(res.directives.b).toBe(b)
+    expect(res.directives.c).toBe(c)
+    expect(res.directives.d).toBe(d)
+    expect(res.created[0]).toBe(f1)
+    expect(res.created[1]).toBe(f2)
+    expect(res.created[2]).toBe(f3)
+    expect(res.created[3]).toBe(f4)
+  })
+
+  it('Array assets', function () {
+    var a = {
+      components: {
+        a: Vue.extend({})
+      }
+    }
+    var b = {
+      components: [{ name: 'b' }]
+    }
+    var res = merge(a, b)
+    expect(res.components.a).toBe(a.components.a)
+    // b.components is guarded and converted to object hash
+    expect(res.components.b).toBeTruthy()
+    expect(res.components.b).toBe(b.components.b)
+  })
+
+  it('warn Array assets without id', function () {
+    var a = {
+      components: {
+        a: Vue.extend({})
+      }
+    }
+    var b = {
+      components: [{}]
+    }
+    merge(a, b)
+    expect('must provide a "name" or "id" field').toHaveBeenWarned()
+  })
+
+  it('warn Array async component without id', function () {
+    var a = {
+      components: {
+        a: Vue.extend({})
+      }
+    }
+    var b = {
+      components: [function () {}]
+    }
+    merge(a, b)
+    expect('must provide a "name" or "id" field').toHaveBeenWarned()
+  })
+})
+
+describe('Util - Option resolveAsset', function () {
+  var vm
+  beforeEach(function () {
+    vm = new Vue({
+      data: {},
+      components: {
+        'hyphenated-component': {
+          template: 'foo'
+        },
+        camelCasedComponent: {
+          template: 'bar'
+        },
+        PascalCasedComponent: {
+          template: 'baz'
+        }
+      }
+    })
+  })
+
+  it('resolves', function () {
+    expect(resolveAsset(vm.$options, 'components', 'hyphenated-component')).toBeTruthy()
+    expect(resolveAsset(vm.$options, 'components', 'camel-cased-component')).toBeTruthy()
+    expect(resolveAsset(vm.$options, 'components', 'pascal-cased-component')).toBeTruthy()
+  })
+})
diff --git a/test/unit/specs/watcher_spec.js b/test/unit/specs/watcher_spec.js
new file mode 100644
index 00000000000..2450417ce90
--- /dev/null
+++ b/test/unit/specs/watcher_spec.js
@@ -0,0 +1,391 @@
+var Vue = require('src')
+var nextTick = Vue.nextTick
+var Watcher = require('src/watcher')
+var _ = Vue.util
+var config = Vue.config
+
+describe('Watcher', function () {
+  var vm, spy
+  beforeEach(function () {
+    vm = new Vue({
+      filters: {},
+      data: {
+        a: 1,
+        b: {
+          c: 2,
+          d: 4
+        },
+        c: 'c',
+        msg: 'yo'
+      }
+    })
+    spy = jasmine.createSpy('watcher')
+  })
+
+  it('simple path', function (done) {
+    var watcher = new Watcher(vm, 'b.c', spy)
+    expect(watcher.value).toBe(2)
+    vm.b.c = 3
+    nextTick(function () {
+      expect(watcher.value).toBe(3)
+      expect(spy).toHaveBeenCalledWith(3, 2)
+      vm.b = { c: 4 } // swapping the object
+      nextTick(function () {
+        expect(watcher.value).toBe(4)
+        expect(spy).toHaveBeenCalledWith(4, 3)
+        done()
+      })
+    })
+  })
+
+  it('bracket access path', function (done) {
+    var watcher = new Watcher(vm, 'b["c"]', spy)
+    expect(watcher.value).toBe(2)
+    vm.b.c = 3
+    nextTick(function () {
+      expect(watcher.value).toBe(3)
+      expect(spy).toHaveBeenCalledWith(3, 2)
+      vm.b = { c: 4 } // swapping the object
+      nextTick(function () {
+        expect(watcher.value).toBe(4)
+        expect(spy).toHaveBeenCalledWith(4, 3)
+        done()
+      })
+    })
+  })
+
+  it('dynamic path', function (done) {
+    var watcher = new Watcher(vm, 'b[c]', spy)
+    expect(watcher.value).toBe(2)
+    vm.b.c = 3
+    nextTick(function () {
+      expect(watcher.value).toBe(3)
+      expect(spy).toHaveBeenCalledWith(3, 2)
+      vm.c = 'd' // changing the dynamic segment in path
+      nextTick(function () {
+        expect(watcher.value).toBe(4)
+        expect(spy).toHaveBeenCalledWith(4, 3)
+        done()
+      })
+    })
+  })
+
+  it('simple expression', function (done) {
+    var watcher = new Watcher(vm, 'a + b.c', spy)
+    expect(watcher.value).toBe(3)
+    vm.b.c = 3
+    nextTick(function () {
+      expect(watcher.value).toBe(4)
+      expect(spy.calls.count()).toBe(1)
+      expect(spy).toHaveBeenCalledWith(4, 3)
+      // change two dependencies at once
+      vm.a = 2
+      vm.b.c = 4
+      nextTick(function () {
+        expect(watcher.value).toBe(6)
+        // should trigger only once callback,
+        // because it was in the same event loop.
+        expect(spy.calls.count()).toBe(2)
+        expect(spy).toHaveBeenCalledWith(6, 4)
+        done()
+      })
+    })
+  })
+
+  it('ternary expression', function (done) {
+    // we're actually testing for the dependency re-calculation here
+    var watcher = new Watcher(vm, 'a > 1 ? b.c : b.d', spy)
+    expect(watcher.value).toBe(4)
+    vm.a = 2
+    nextTick(function () {
+      expect(watcher.value).toBe(2)
+      expect(spy).toHaveBeenCalledWith(2, 4)
+      vm.b.c = 3
+      nextTick(function () {
+        expect(watcher.value).toBe(3)
+        expect(spy).toHaveBeenCalledWith(3, 2)
+        done()
+      })
+    })
+  })
+
+  it('meta properties', function (done) {
+    _.defineReactive(vm, '$index', 1)
+    var watcher = new Watcher(vm, '$index + 1', spy)
+    expect(watcher.value).toBe(2)
+    vm.$index = 2
+    nextTick(function () {
+      expect(watcher.value).toBe(3)
+      done()
+    })
+  })
+
+  it('non-existent path, set later', function (done) {
+    var watcher = new Watcher(vm, 'd.e', spy)
+    var watcher2 = new Watcher(vm, 'b.e', spy)
+    expect(watcher.value).toBeUndefined()
+    expect(watcher2.value).toBeUndefined()
+    // check $add should not affect isolated children
+    var child2 = new Vue({ parent: vm })
+    var watcher3 = new Watcher(child2, 'd.e', spy)
+    expect(watcher3.value).toBeUndefined()
+    vm.$set('d', { e: 123 })
+    _.set(vm.b, 'e', 234)
+    nextTick(function () {
+      expect(watcher.value).toBe(123)
+      expect(watcher2.value).toBe(234)
+      expect(watcher3.value).toBeUndefined()
+      expect(spy.calls.count()).toBe(2)
+      expect(spy).toHaveBeenCalledWith(123, undefined)
+      expect(spy).toHaveBeenCalledWith(234, undefined)
+      done()
+    })
+  })
+
+  it('$delete', function (done) {
+    var watcher = new Watcher(vm, 'b.c', spy)
+    expect(watcher.value).toBe(2)
+    vm.$delete('b')
+    nextTick(function () {
+      expect(watcher.value).toBeUndefined()
+      expect(spy).toHaveBeenCalledWith(undefined, 2)
+      done()
+    })
+  })
+
+  it('swapping $data', function (done) {
+    // existing path
+    var watcher = new Watcher(vm, 'b.c', spy)
+    var spy2 = jasmine.createSpy()
+    // non-existing path
+    var watcher2 = new Watcher(vm, 'e', spy2)
+    expect(watcher.value).toBe(2)
+    expect(watcher2.value).toBeUndefined()
+    vm.$data = { b: { c: 3 }, e: 4 }
+    nextTick(function () {
+      expect(watcher.value).toBe(3)
+      expect(watcher2.value).toBe(4)
+      expect(spy).toHaveBeenCalledWith(3, 2)
+      expect(spy2).toHaveBeenCalledWith(4, undefined)
+      done()
+    })
+  })
+
+  it('path containing $data', function (done) {
+    var watcher = new Watcher(vm, '$data.b.c', spy)
+    expect(watcher.value).toBe(2)
+    vm.b = { c: 3 }
+    nextTick(function () {
+      expect(watcher.value).toBe(3)
+      expect(spy).toHaveBeenCalledWith(3, 2)
+      vm.$data = { b: { c: 4 }}
+      nextTick(function () {
+        expect(watcher.value).toBe(4)
+        expect(spy).toHaveBeenCalledWith(4, 3)
+        done()
+      })
+    })
+  })
+
+  it('watching $data', function (done) {
+    var oldData = vm.$data
+    var watcher = new Watcher(vm, '$data', spy)
+    expect(watcher.value).toBe(oldData)
+    var newData = {}
+    vm.$data = newData
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(newData, oldData)
+      expect(watcher.value).toBe(newData)
+      done()
+    })
+  })
+
+  it('filters', function (done) {
+    vm.$options.filters.test = function (val, multi) {
+      return val * multi
+    }
+    vm.$options.filters.test2 = function (val, str) {
+      return val + str
+    }
+    var watcher = new Watcher(vm, 'b.c', spy, {
+      filters: [
+        { name: 'test', args: [{ value: 3, dynamic: false }] },
+        { name: 'test2', args: [{ value: 'msg', dynamic: true }] }
+      ]
+    })
+    expect(watcher.value).toBe('6yo')
+    vm.b.c = 3
+    nextTick(function () {
+      expect(watcher.value).toBe('9yo')
+      expect(spy).toHaveBeenCalledWith('9yo', '6yo')
+      done()
+    })
+  })
+
+  it('setter', function (done) {
+    vm.$options.filters.test = {
+      write: function (val, oldVal, arg) {
+        return val > arg ? val : oldVal
+      }
+    }
+    var watcher = new Watcher(vm, 'b["c"]', spy, {
+      filters: [
+        { name: 'test', args: [{value: 5, dynamic: false}] }
+      ],
+      twoWay: true
+    })
+    expect(watcher.value).toBe(2)
+    watcher.set(4) // shoud not change the value
+    nextTick(function () {
+      expect(vm.b.c).toBe(2)
+      expect(watcher.value).toBe(2)
+      expect(spy).not.toHaveBeenCalled()
+      watcher.set(6)
+      nextTick(function () {
+        expect(vm.b.c).toBe(6)
+        expect(watcher.value).toBe(6)
+        expect(spy).toHaveBeenCalledWith(6, 2)
+        done()
+      })
+    })
+  })
+
+  it('set non-existent values', function (done) {
+    var watcher = new Watcher(vm, 'd.e.f', spy, {
+      twoWay: true
+    })
+    expect(watcher.value).toBeUndefined()
+    watcher.set(123)
+    nextTick(function () {
+      expect(vm.d.e.f).toBe(123)
+      expect(watcher.value).toBe(123)
+      expect(spy).toHaveBeenCalledWith(123, undefined)
+      done()
+    })
+  })
+
+  it('deep watch', function (done) {
+    new Watcher(vm, 'b', spy, {
+      deep: true
+    })
+    vm.b.c = { d: 4 }
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
+      var oldB = vm.b
+      vm.b = { c: [{ a: 1 }]}
+      nextTick(function () {
+        expect(spy).toHaveBeenCalledWith(vm.b, oldB)
+        expect(spy.calls.count()).toBe(2)
+        vm.b.c[0].a = 2
+        nextTick(function () {
+          expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
+          expect(spy.calls.count()).toBe(3)
+          done()
+        })
+      })
+    })
+  })
+
+  it('deep watch with circular references', function (done) {
+    new Watcher(vm, 'b', spy, {
+      deep: true
+    })
+    Vue.set(vm.b, '_', vm.b)
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
+      expect(spy.calls.count()).toBe(1)
+      vm.b._.c = 1
+      nextTick(function () {
+        expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
+        expect(spy.calls.count()).toBe(2)
+        done()
+      })
+    })
+  })
+
+  it('fire change for prop addition/deletion in non-deep mode', function (done) {
+    new Watcher(vm, 'b', spy)
+    Vue.set(vm.b, 'e', 123)
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(vm.b, vm.b)
+      expect(spy.calls.count()).toBe(1)
+      Vue.delete(vm.b, 'e')
+      nextTick(function () {
+        expect(spy.calls.count()).toBe(2)
+        done()
+      })
+    })
+  })
+
+  it('watch function', function (done) {
+    var watcher = new Watcher(vm, function () {
+      return this.a + this.b.d
+    }, spy)
+    expect(watcher.value).toBe(5)
+    vm.a = 2
+    nextTick(function () {
+      expect(spy).toHaveBeenCalledWith(6, 5)
+      vm.b = { d: 2 }
+      nextTick(function () {
+        expect(spy).toHaveBeenCalledWith(4, 6)
+        done()
+      })
+    })
+  })
+
+  it('lazy mode', function (done) {
+    var watcher = new Watcher(vm, function () {
+      return this.a + this.b.d
+    }, null, { lazy: true })
+    expect(watcher.lazy).toBe(true)
+    expect(watcher.value).toBeUndefined()
+    expect(watcher.dirty).toBe(true)
+    watcher.evaluate()
+    expect(watcher.value).toBe(5)
+    expect(watcher.dirty).toBe(false)
+    vm.a = 2
+    nextTick(function () {
+      expect(watcher.value).toBe(5)
+      expect(watcher.dirty).toBe(true)
+      watcher.evaluate()
+      expect(watcher.value).toBe(6)
+      expect(watcher.dirty).toBe(false)
+      done()
+    })
+  })
+
+  it('teardown', function (done) {
+    var watcher = new Watcher(vm, 'b.c', spy)
+    watcher.teardown()
+    vm.b.c = 3
+    nextTick(function () {
+      expect(watcher.active).toBe(false)
+      expect(watcher.vm).toBe(null)
+      expect(watcher.cb).toBe(null)
+      expect(spy).not.toHaveBeenCalled()
+      done()
+    })
+  })
+
+  it('synchronous updates', function () {
+    config.async = false
+    new Watcher(vm, 'a', spy)
+    vm.a = 2
+    vm.a = 3
+    expect(spy.calls.count()).toBe(2)
+    expect(spy).toHaveBeenCalledWith(2, 1)
+    expect(spy).toHaveBeenCalledWith(3, 2)
+    config.async = true
+  })
+
+  it('warn getter errors', function () {
+    new Watcher(vm, 'd.e + c', spy)
+    expect('Error when evaluating expression').toHaveBeenWarned()
+  })
+
+  it('warn setter errors', function () {
+    var watcher = new Watcher(vm, 'a + b', spy)
+    watcher.set(123)
+    expect('Error when evaluating setter').toHaveBeenWarned()
+  })
+})
diff --git a/test/vitest.setup.ts b/test/vitest.setup.ts
deleted file mode 100644
index 38397765f06..00000000000
--- a/test/vitest.setup.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-process.env.NEW_SLOT_SYNTAX = 'true'
-
-import './helpers/shim-done'
-import './helpers/to-have-warned'
-import './helpers/classlist'
-
-import { waitForUpdate } from './helpers/wait-for-update'
-import { triggerEvent } from './helpers/trigger-event'
-import { createTextVNode } from './helpers/vdom'
-
-global.waitForUpdate = waitForUpdate
-global.triggerEvent = triggerEvent
-global.createTextVNode = createTextVNode
diff --git a/tsconfig.json b/tsconfig.json
deleted file mode 100644
index 214cb46c7c0..00000000000
--- a/tsconfig.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "compilerOptions": {
-    "baseUrl": ".",
-    "outDir": "dist",
-    "sourceMap": true,
-    "target": "esnext",
-    "module": "ESNext",
-    "moduleResolution": "node",
-    "newLine": "LF",
-    "strict": true,
-
-    "allowJs": true,
-    "noImplicitAny": false,
-    "noImplicitThis": false,
-
-    "noUnusedLocals": true,
-    "experimentalDecorators": true,
-    "resolveJsonModule": true,
-    "esModuleInterop": true,
-    "removeComments": false,
-    "jsx": "preserve",
-    "lib": ["esnext", "dom"],
-    "types": ["node"],
-    "paths": {
-      "compiler/*": ["src/compiler/*"],
-      "core/*": ["src/core/*"],
-      "server/*": ["packages/server-renderer/src/*"],
-      "sfc/*": ["packages/compiler-sfc/src/*"],
-      "shared/*": ["src/shared/*"],
-      "web/*": ["src/platforms/web/*"],
-      "v3": ["src/v3/index"],
-      "v3/*": ["src/v3/*"],
-      "types/*": ["src/types/*"],
-      "vue": ["src/platforms/web/entry-runtime-with-compiler"]
-    }
-  },
-  "include": ["src", "packages/*/src"]
-}
diff --git a/types/built-in-components.d.ts b/types/built-in-components.d.ts
deleted file mode 100644
index cad116b3dae..00000000000
--- a/types/built-in-components.d.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { DefineComponent } from './v3-define-component'
-
-type Hook<T = () => void> = T | T[]
-
-export interface TransitionProps {
-  name?: string
-  appear?: boolean
-  css?: boolean
-  mode?: 'in-out' | 'out-in' | 'default'
-  type?: 'transition' | 'animation'
-
-  duration?:
-    | number
-    | string
-    | {
-        enter: number
-        leave: number
-      }
-
-  // classes
-  enterClass?: string
-  enterActiveClass?: string
-  enterToClass?: string
-  appearClass?: string
-  appearActiveClass?: string
-  appearToClass?: string
-  leaveClass?: string
-  leaveActiveClass?: string
-  leaveToClass?: string
-
-  // event hooks
-  onBeforeEnter?: Hook<(el: Element) => void>
-  onEnter?: Hook<(el: Element, done: () => void) => void>
-  onAfterEnter?: Hook<(el: Element) => void>
-  onEnterCancelled?: Hook<(el: Element) => void>
-  onBeforeLeave?: Hook<(el: Element) => void>
-  onLeave?: Hook<(el: Element, done: () => void) => void>
-  onAfterLeave?: Hook<(el: Element) => void>
-  onLeaveCancelled?: Hook<(el: Element) => void>
-  onBeforeAppear?: Hook<(el: Element) => void>
-  onAppear?: Hook<(el: Element, done: () => void) => void>
-  onAfterAppear?: Hook<(el: Element) => void>
-  onAppearCancelled?: Hook<(el: Element) => void>
-}
-
-export declare const Transition: DefineComponent<TransitionProps>
-
-export type TransitionGroupProps = Omit<TransitionProps, 'mode'> & {
-  tag?: string
-  moveClass?: string
-}
-
-export declare const TransitionGroup: DefineComponent<TransitionGroupProps>
-
-type MatchPattern = string | RegExp | (string | RegExp)[]
-
-export interface KeepAliveProps {
-  include?: MatchPattern
-  exclude?: MatchPattern
-  max?: number | string
-}
-
-export declare const KeepAlive: DefineComponent<KeepAliveProps>
diff --git a/types/common.d.ts b/types/common.d.ts
deleted file mode 100644
index 01d1efbfc7a..00000000000
--- a/types/common.d.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-export type Data = { [key: string]: unknown }
-
-export type UnionToIntersection<U> = (
-  U extends any ? (k: U) => void : never
-) extends (k: infer I) => void
-  ? I
-  : never
-
-// Conditional returns can enforce identical types.
-// See here: https://github.com/Microsoft/TypeScript/issues/27024#issuecomment-421529650
-// prettier-ignore
-type Equal<Left, Right> =
-  (<U>() => U extends Left ? 1 : 0) extends (<U>() => U extends Right ? 1 : 0) ? true : false;
-
-export type HasDefined<T> = Equal<T, unknown> extends true ? false : true
-
-// If the type T accepts type "any", output type Y, otherwise output type N.
-// https://stackoverflow.com/questions/49927523/disallow-call-with-any/49928360#49928360
-export type IfAny<T, Y, N> = 0 extends 1 & T ? Y : N
-
-export type LooseRequired<T> = { [P in string & keyof T]: T[P] }
diff --git a/types/index.d.ts b/types/index.d.ts
index 779ff922a56..a5a817849fe 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1,81 +1,51 @@
-import { Vue } from './vue'
-import './umd'
-import './jsx'
-export * from './jsx'
-
-export default Vue
-
-export { CreateElement, VueConstructor } from './vue'
-
-export {
-  Component,
-  AsyncComponent,
-  ComponentOptions,
-  FunctionalComponentOptions,
-  RenderContext,
-  PropType,
-  PropOptions,
-  ComputedOptions,
-  WatchHandler,
-  WatchOptions,
-  WatchOptionsWithHandler,
-  DirectiveFunction,
-  DirectiveOptions
-} from './options'
-
-export { PluginFunction, PluginObject } from './plugin'
-
-export {
-  VNodeChildren,
-  VNodeChildrenArrayContents,
-  VNode,
-  VNodeComponentOptions,
-  VNodeData,
-  VNodeDirective,
-  ComponentCustomProps
-} from './vnode'
-
-export * from './v3-manual-apis'
-export * from './v3-generated'
-// <script setup> helpers
-export * from './v3-setup-helpers'
-
-export { Data } from './common'
-export { SetupContext } from './v3-setup-context'
-export { defineComponent, DefineComponent } from './v3-define-component'
-export { defineAsyncComponent } from './v3-define-async-component'
-export {
-  SetupFunction,
-  // v2 already has option with same name and it's for a single computed
-  ComputedOptions as ComponentComputedOptions,
-  MethodOptions as ComponentMethodOptions,
-  ComponentPropsOptions,
-  ComponentCustomOptions,
-  ComponentOptionsMixin,
-  ComponentOptionsWithoutProps,
-  ComponentOptionsWithArrayProps,
-  ComponentOptionsWithProps,
-  ComponentOptionsBase
-} from './v3-component-options'
-export {
-  ComponentInstance,
-  ComponentPublicInstance,
-  CreateComponentPublicInstance,
-  ComponentCustomProperties
-} from './v3-component-public-instance'
-export {
-  // PropType,
-  // PropOptions,
-  ExtractPropTypes,
-  ExtractDefaultPropTypes
-} from './v3-component-props'
-export {
-  DirectiveModifiers,
-  DirectiveBinding,
-  DirectiveHook,
-  ObjectDirective,
-  FunctionDirective,
-  Directive
-} from './v3-directive'
-
-export * from './built-in-components'
+import * as V from './vue';
+import * as Options from './options';
+import * as Plugin from './plugin';
+
+declare global {
+  interface Array<T> {
+    $remove(item: T): Array<T>;
+    $set(index: any, val: T): T;
+  }
+
+  // For the projects/tools that depend on this namespace
+  namespace vuejs {
+    export type PropOption = Options.PropOptions;
+    export type ComputedOption = Options.ComputedOptions<any>;
+    export type WatchOption = Options.WatchOptions;
+    export type DirectiveOption = Options.DirectiveOptions<Vue>;
+    export type Directive = Options.DirectiveInstance<Vue>;
+    export type TransitionOpsion = Options.TransitionOptions;
+    export type ComponentOption = Options.ComponentOptions<any>;
+    export type FilterOption = Options.FilterOptions;
+    export type Vue = V.Vue;
+    export type VueStatic = typeof V.Vue;
+    export type VueConfig = typeof V.Vue.config;
+  }
+}
+
+// `Vue` in `export = Vue` must be a namespace
+// All available types are exported via this namespace
+declare namespace Vue {
+
+  export type Component = Options.Component;
+  export type AsyncComponent = Options.AsyncComponent;
+  export type ComponentOptions<V extends Vue> = Options.ComponentOptions<V>;
+  export type PropOptions = Options.PropOptions;
+  export type ComputedOptions<V extends Vue> = Options.ComputedOptions<V>;
+  export type WatchHandler<V extends Vue> = Options.WatchHandler<V>;
+  export type WatchOptions = Options.WatchOptions;
+  export type DirectiveInstance<V extends Vue> = Options.DirectiveInstance<V>;
+  export type DirectiveFunction<V extends Vue> = Options.DirectiveFunction<V>;
+  export type DirectiveOptions<V extends Vue> = Options.DirectiveOptions<V>;
+  export type FilterOptions = Options.FilterOptions;
+  export type TransitionOpsions = Options.TransitionOptions;
+
+  export type PluginFunction<T> = Plugin.PluginFunction<T>;
+  export type PluginObject<T> = Plugin.PluginObject<T>;
+}
+
+// TS cannot merge imported class with namespace, declare a subclass to bypass
+declare class Vue extends V.Vue { }
+
+export = Vue;
diff --git a/types/jsx.d.ts b/types/jsx.d.ts
deleted file mode 100644
index 845cfdcb030..00000000000
--- a/types/jsx.d.ts
+++ /dev/null
@@ -1,1353 +0,0 @@
-// This code is based on react definition in DefinitelyTyped published under the MIT license.
-//      Repository: https://github.com/DefinitelyTyped/DefinitelyTyped
-//      Path in the repository: types/react/index.d.ts
-//
-// Copyrights of original definition are:
-//      AssureSign <http://www.assuresign.com>
-//      Microsoft <https://microsoft.com>
-//      John Reilly <https://github.com/johnnyreilly>
-//      Benoit Benezech <https://github.com/bbenezech>
-//      Patricio Zavolinsky <https://github.com/pzavolinsky>
-//      Digiguru <https://github.com/digiguru>
-//      Eric Anderson <https://github.com/ericanderson>
-//      Dovydas Navickas <https://github.com/DovydasNavickas>
-//      Josh Rutherford <https://github.com/theruther4d>
-//      Guilherme Hübner <https://github.com/guilhermehubner>
-//      Ferdy Budhidharma <https://github.com/ferdaber>
-//      Johann Rakotoharisoa <https://github.com/jrakotoharisoa>
-//      Olivier Pascal <https://github.com/pascaloliv>
-//      Martin Hochel <https://github.com/hotell>
-//      Frank Li <https://github.com/franklixuefei>
-//      Jessica Franco <https://github.com/Jessidhia>
-//      Saransh Kataria <https://github.com/saranshkataria>
-//      Kanitkorn Sujautra <https://github.com/lukyth>
-//      Sebastian Silbermann <https://github.com/eps1lon>
-
-import * as CSS from 'csstype'
-
-export interface CSSProperties
-  extends CSS.Properties<string | number>,
-    CSS.PropertiesHyphen<string | number> {
-  /**
-   * The index signature was removed to enable closed typing for style
-   * using CSSType. You're able to use type assertion or module augmentation
-   * to add properties or an index signature of your own.
-   *
-   * For examples and more information, visit:
-   * https://github.com/frenic/csstype#what-should-i-do-when-i-get-type-errors
-   */
-  [v: `--${string}`]: string | number | undefined
-}
-
-type Booleanish = boolean | 'true' | 'false'
-type Numberish = number | string
-
-// All the WAI-ARIA 1.1 attributes from https://www.w3.org/TR/wai-aria-1.1/
-interface AriaAttributes {
-  /** Identifies the currently active element when DOM focus is on a composite widget, textbox, group, or application. */
-  'aria-activedescendant'?: string
-  /** Indicates whether assistive technologies will present all, or only parts of, the changed region based on the change notifications defined by the aria-relevant attribute. */
-  'aria-atomic'?: Booleanish
-  /**
-   * Indicates whether inputting text could trigger display of one or more predictions of the user's intended value for an input and specifies how predictions would be
-   * presented if they are made.
-   */
-  'aria-autocomplete'?: 'none' | 'inline' | 'list' | 'both'
-  /** Indicates an element is being modified and that assistive technologies MAY want to wait until the modifications are complete before exposing them to the user. */
-  'aria-busy'?: Booleanish
-  /**
-   * Indicates the current "checked" state of checkboxes, radio buttons, and other widgets.
-   * @see aria-pressed @see aria-selected.
-   */
-  'aria-checked'?: Booleanish | 'mixed'
-  /**
-   * Defines the total number of columns in a table, grid, or treegrid.
-   * @see aria-colindex.
-   */
-  'aria-colcount'?: Numberish
-  /**
-   * Defines an element's column index or position with respect to the total number of columns within a table, grid, or treegrid.
-   * @see aria-colcount @see aria-colspan.
-   */
-  'aria-colindex'?: Numberish
-  /**
-   * Defines the number of columns spanned by a cell or gridcell within a table, grid, or treegrid.
-   * @see aria-colindex @see aria-rowspan.
-   */
-  'aria-colspan'?: Numberish
-  /**
-   * Identifies the element (or elements) whose contents or presence are controlled by the current element.
-   * @see aria-owns.
-   */
-  'aria-controls'?: string
-  /** Indicates the element that represents the current item within a container or set of related elements. */
-  'aria-current'?: Booleanish | 'page' | 'step' | 'location' | 'date' | 'time'
-  /**
-   * Identifies the element (or elements) that describes the object.
-   * @see aria-labelledby
-   */
-  'aria-describedby'?: string
-  /**
-   * Identifies the element that provides a detailed, extended description for the object.
-   * @see aria-describedby.
-   */
-  'aria-details'?: string
-  /**
-   * Indicates that the element is perceivable but disabled, so it is not editable or otherwise operable.
-   * @see aria-hidden @see aria-readonly.
-   */
-  'aria-disabled'?: Booleanish
-  /**
-   * Indicates what functions can be performed when a dragged object is released on the drop target.
-   * @deprecated in ARIA 1.1
-   */
-  'aria-dropeffect'?: 'none' | 'copy' | 'execute' | 'link' | 'move' | 'popup'
-  /**
-   * Identifies the element that provides an error message for the object.
-   * @see aria-invalid @see aria-describedby.
-   */
-  'aria-errormessage'?: string
-  /** Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed. */
-  'aria-expanded'?: Booleanish
-  /**
-   * Identifies the next element (or elements) in an alternate reading order of content which, at the user's discretion,
-   * allows assistive technology to override the general default of reading in document source order.
-   */
-  'aria-flowto'?: string
-  /**
-   * Indicates an element's "grabbed" state in a drag-and-drop operation.
-   * @deprecated in ARIA 1.1
-   */
-  'aria-grabbed'?: Booleanish
-  /** Indicates the availability and type of interactive popup element, such as menu or dialog, that can be triggered by an element. */
-  'aria-haspopup'?: Booleanish | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog'
-  /**
-   * Indicates whether the element is exposed to an accessibility API.
-   * @see aria-disabled.
-   */
-  'aria-hidden'?: Booleanish
-  /**
-   * Indicates the entered value does not conform to the format expected by the application.
-   * @see aria-errormessage.
-   */
-  'aria-invalid'?: Booleanish | 'grammar' | 'spelling'
-  /** Indicates keyboard shortcuts that an author has implemented to activate or give focus to an element. */
-  'aria-keyshortcuts'?: string
-  /**
-   * Defines a string value that labels the current element.
-   * @see aria-labelledby.
-   */
-  'aria-label'?: string
-  /**
-   * Identifies the element (or elements) that labels the current element.
-   * @see aria-describedby.
-   */
-  'aria-labelledby'?: string
-  /** Defines the hierarchical level of an element within a structure. */
-  'aria-level'?: Numberish
-  /** Indicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region. */
-  'aria-live'?: 'off' | 'assertive' | 'polite'
-  /** Indicates whether an element is modal when displayed. */
-  'aria-modal'?: Booleanish
-  /** Indicates whether a text box accepts multiple lines of input or only a single line. */
-  'aria-multiline'?: Booleanish
-  /** Indicates that the user may select more than one item from the current selectable descendants. */
-  'aria-multiselectable'?: Booleanish
-  /** Indicates whether the element's orientation is horizontal, vertical, or unknown/ambiguous. */
-  'aria-orientation'?: 'horizontal' | 'vertical'
-  /**
-   * Identifies an element (or elements) in order to define a visual, functional, or contextual parent/child relationship
-   * between DOM elements where the DOM hierarchy cannot be used to represent the relationship.
-   * @see aria-controls.
-   */
-  'aria-owns'?: string
-  /**
-   * Defines a short hint (a word or short phrase) intended to aid the user with data entry when the control has no value.
-   * A hint could be a sample value or a brief description of the expected format.
-   */
-  'aria-placeholder'?: string
-  /**
-   * Defines an element's number or position in the current set of listitems or treeitems. Not required if all elements in the set are present in the DOM.
-   * @see aria-setsize.
-   */
-  'aria-posinset'?: Numberish
-  /**
-   * Indicates the current "pressed" state of toggle buttons.
-   * @see aria-checked @see aria-selected.
-   */
-  'aria-pressed'?: Booleanish | 'mixed'
-  /**
-   * Indicates that the element is not editable, but is otherwise operable.
-   * @see aria-disabled.
-   */
-  'aria-readonly'?: Booleanish
-  /**
-   * Indicates what notifications the user agent will trigger when the accessibility tree within a live region is modified.
-   * @see aria-atomic.
-   */
-  'aria-relevant'?: 'additions' | 'additions text' | 'all' | 'removals' | 'text'
-  /** Indicates that user input is required on the element before a form may be submitted. */
-  'aria-required'?: Booleanish
-  /** Defines a human-readable, author-localized description for the role of an element. */
-  'aria-roledescription'?: string
-  /**
-   * Defines the total number of rows in a table, grid, or treegrid.
-   * @see aria-rowindex.
-   */
-  'aria-rowcount'?: Numberish
-  /**
-   * Defines an element's row index or position with respect to the total number of rows within a table, grid, or treegrid.
-   * @see aria-rowcount @see aria-rowspan.
-   */
-  'aria-rowindex'?: Numberish
-  /**
-   * Defines the number of rows spanned by a cell or gridcell within a table, grid, or treegrid.
-   * @see aria-rowindex @see aria-colspan.
-   */
-  'aria-rowspan'?: Numberish
-  /**
-   * Indicates the current "selected" state of various widgets.
-   * @see aria-checked @see aria-pressed.
-   */
-  'aria-selected'?: Booleanish
-  /**
-   * Defines the number of items in the current set of listitems or treeitems. Not required if all elements in the set are present in the DOM.
-   * @see aria-posinset.
-   */
-  'aria-setsize'?: Numberish
-  /** Indicates if items in a table or grid are sorted in ascending or descending order. */
-  'aria-sort'?: 'none' | 'ascending' | 'descending' | 'other'
-  /** Defines the maximum allowed value for a range widget. */
-  'aria-valuemax'?: Numberish
-  /** Defines the minimum allowed value for a range widget. */
-  'aria-valuemin'?: Numberish
-  /**
-   * Defines the current value for a range widget.
-   * @see aria-valuetext.
-   */
-  'aria-valuenow'?: Numberish
-  /** Defines the human readable text alternative of aria-valuenow for a range widget. */
-  'aria-valuetext'?: string
-}
-
-// Vue's style normalization supports nested arrays
-export type StyleValue = string | CSSProperties | Array<StyleValue>
-
-export interface HTMLAttributes extends AriaAttributes, EventHandlers<Events> {
-  innerHTML?: string
-
-  class?: any
-  style?: StyleValue
-
-  // Standard HTML Attributes
-  accesskey?: string
-  contenteditable?: Booleanish | 'inherit'
-  contextmenu?: string
-  dir?: string
-  draggable?: Booleanish
-  hidden?: Booleanish
-  id?: string
-  lang?: string
-  placeholder?: string
-  spellcheck?: Booleanish
-  tabindex?: Numberish
-  title?: string
-  translate?: 'yes' | 'no'
-
-  // Unknown
-  radiogroup?: string // <command>, <menuitem>
-
-  // WAI-ARIA
-  role?: string
-
-  // RDFa Attributes
-  about?: string
-  datatype?: string
-  inlist?: any
-  prefix?: string
-  property?: string
-  resource?: string
-  typeof?: string
-  vocab?: string
-
-  // Non-standard Attributes
-  autocapitalize?: string
-  autocorrect?: string
-  autosave?: string
-  color?: string
-  itemprop?: string
-  itemscope?: Booleanish
-  itemtype?: string
-  itemid?: string
-  itemref?: string
-  results?: Numberish
-  security?: string
-  unselectable?: 'on' | 'off'
-
-  // Living Standard
-  /**
-   * Hints at the type of data that might be entered by the user while editing the element or its contents
-   * @see https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-inputmode-attribute
-   */
-  inputmode?:
-    | 'none'
-    | 'text'
-    | 'tel'
-    | 'url'
-    | 'email'
-    | 'numeric'
-    | 'decimal'
-    | 'search'
-  /**
-   * Specify that a standard HTML element should behave like a defined custom built-in element
-   * @see https://html.spec.whatwg.org/multipage/custom-elements.html#attr-is
-   */
-  is?: string
-}
-
-export interface AnchorHTMLAttributes extends HTMLAttributes {
-  download?: any
-  href?: string
-  hreflang?: string
-  media?: string
-  ping?: string
-  rel?: string
-  target?: string
-  type?: string
-  referrerpolicy?: string
-}
-
-export interface AreaHTMLAttributes extends HTMLAttributes {
-  alt?: string
-  coords?: string
-  download?: any
-  href?: string
-  hreflang?: string
-  media?: string
-  rel?: string
-  shape?: string
-  target?: string
-}
-
-export interface AudioHTMLAttributes extends MediaHTMLAttributes {}
-
-export interface BaseHTMLAttributes extends HTMLAttributes {
-  href?: string
-  target?: string
-}
-
-export interface BlockquoteHTMLAttributes extends HTMLAttributes {
-  cite?: string
-}
-
-export interface ButtonHTMLAttributes extends HTMLAttributes {
-  autofocus?: Booleanish
-  disabled?: Booleanish
-  form?: string
-  formaction?: string
-  formenctype?: string
-  formmethod?: string
-  formnovalidate?: Booleanish
-  formtarget?: string
-  name?: string
-  type?: 'submit' | 'reset' | 'button'
-  value?: string | string[] | number
-}
-
-export interface CanvasHTMLAttributes extends HTMLAttributes {
-  height?: Numberish
-  width?: Numberish
-}
-
-export interface ColHTMLAttributes extends HTMLAttributes {
-  span?: Numberish
-  width?: Numberish
-}
-
-export interface ColgroupHTMLAttributes extends HTMLAttributes {
-  span?: Numberish
-}
-
-export interface DataHTMLAttributes extends HTMLAttributes {
-  value?: string | string[] | number
-}
-
-export interface DetailsHTMLAttributes extends HTMLAttributes {
-  open?: Booleanish
-}
-
-export interface DelHTMLAttributes extends HTMLAttributes {
-  cite?: string
-  datetime?: string
-}
-
-export interface DialogHTMLAttributes extends HTMLAttributes {
-  open?: Booleanish
-}
-
-export interface EmbedHTMLAttributes extends HTMLAttributes {
-  height?: Numberish
-  src?: string
-  type?: string
-  width?: Numberish
-}
-
-export interface FieldsetHTMLAttributes extends HTMLAttributes {
-  disabled?: Booleanish
-  form?: string
-  name?: string
-}
-
-export interface FormHTMLAttributes extends HTMLAttributes {
-  acceptcharset?: string
-  action?: string
-  autocomplete?: string
-  enctype?: string
-  method?: string
-  name?: string
-  novalidate?: Booleanish
-  target?: string
-}
-
-export interface HtmlHTMLAttributes extends HTMLAttributes {
-  manifest?: string
-}
-
-export interface IframeHTMLAttributes extends HTMLAttributes {
-  allow?: string
-  allowfullscreen?: Booleanish
-  allowtransparency?: Booleanish
-  frameborder?: Numberish
-  height?: Numberish
-  marginheight?: Numberish
-  marginwidth?: Numberish
-  name?: string
-  referrerpolicy?: string
-  sandbox?: string
-  scrolling?: string
-  seamless?: Booleanish
-  src?: string
-  srcdoc?: string
-  width?: Numberish
-}
-
-export interface ImgHTMLAttributes extends HTMLAttributes {
-  alt?: string
-  crossorigin?: 'anonymous' | 'use-credentials' | ''
-  decoding?: 'async' | 'auto' | 'sync'
-  height?: Numberish
-  sizes?: string
-  src?: string
-  srcset?: string
-  usemap?: string
-  width?: Numberish
-}
-
-export interface InsHTMLAttributes extends HTMLAttributes {
-  cite?: string
-  datetime?: string
-}
-
-export interface InputHTMLAttributes extends HTMLAttributes {
-  accept?: string
-  alt?: string
-  autocomplete?: string
-  autofocus?: Booleanish
-  capture?: boolean | 'user' | 'environment' // https://www.w3.org/tr/html-media-capture/#the-capture-attribute
-  checked?: Booleanish | any[] | Set<any> // for IDE v-model multi-checkbox support
-  crossorigin?: string
-  disabled?: Booleanish
-  form?: string
-  formaction?: string
-  formenctype?: string
-  formmethod?: string
-  formnovalidate?: Booleanish
-  formtarget?: string
-  height?: Numberish
-  indeterminate?: boolean
-  list?: string
-  max?: Numberish
-  maxlength?: Numberish
-  min?: Numberish
-  minlength?: Numberish
-  multiple?: Booleanish
-  name?: string
-  pattern?: string
-  placeholder?: string
-  readonly?: Booleanish
-  required?: Booleanish
-  size?: Numberish
-  src?: string
-  step?: Numberish
-  type?: string
-  value?: any // we support :value to be bound to anything w/ v-model
-  width?: Numberish
-}
-
-export interface KeygenHTMLAttributes extends HTMLAttributes {
-  autofocus?: Booleanish
-  challenge?: string
-  disabled?: Booleanish
-  form?: string
-  keytype?: string
-  keyparams?: string
-  name?: string
-}
-
-export interface LabelHTMLAttributes extends HTMLAttributes {
-  for?: string
-  form?: string
-}
-
-export interface LiHTMLAttributes extends HTMLAttributes {
-  value?: string | string[] | number
-}
-
-export interface LinkHTMLAttributes extends HTMLAttributes {
-  as?: string
-  crossorigin?: string
-  href?: string
-  hreflang?: string
-  integrity?: string
-  media?: string
-  rel?: string
-  sizes?: string
-  type?: string
-}
-
-export interface MapHTMLAttributes extends HTMLAttributes {
-  name?: string
-}
-
-export interface MenuHTMLAttributes extends HTMLAttributes {
-  type?: string
-}
-
-export interface MediaHTMLAttributes extends HTMLAttributes {
-  autoplay?: Booleanish
-  controls?: Booleanish
-  controlslist?: string
-  crossorigin?: string
-  loop?: Booleanish
-  mediagroup?: string
-  muted?: Booleanish
-  playsinline?: Booleanish
-  preload?: string
-  src?: string
-}
-
-export interface MetaHTMLAttributes extends HTMLAttributes {
-  charset?: string
-  content?: string
-  httpequiv?: string
-  name?: string
-}
-
-export interface MeterHTMLAttributes extends HTMLAttributes {
-  form?: string
-  high?: Numberish
-  low?: Numberish
-  max?: Numberish
-  min?: Numberish
-  optimum?: Numberish
-  value?: string | string[] | number
-}
-
-export interface QuoteHTMLAttributes extends HTMLAttributes {
-  cite?: string
-}
-
-export interface ObjectHTMLAttributes extends HTMLAttributes {
-  classid?: string
-  data?: string
-  form?: string
-  height?: Numberish
-  name?: string
-  type?: string
-  usemap?: string
-  width?: Numberish
-  wmode?: string
-}
-
-export interface OlHTMLAttributes extends HTMLAttributes {
-  reversed?: Booleanish
-  start?: Numberish
-  type?: '1' | 'a' | 'A' | 'i' | 'I'
-}
-
-export interface OptgroupHTMLAttributes extends HTMLAttributes {
-  disabled?: Booleanish
-  label?: string
-}
-
-export interface OptionHTMLAttributes extends HTMLAttributes {
-  disabled?: Booleanish
-  label?: string
-  selected?: Booleanish
-  value?: any // we support :value to be bound to anything w/ v-model
-}
-
-export interface OutputHTMLAttributes extends HTMLAttributes {
-  for?: string
-  form?: string
-  name?: string
-}
-
-export interface ParamHTMLAttributes extends HTMLAttributes {
-  name?: string
-  value?: string | string[] | number
-}
-
-export interface ProgressHTMLAttributes extends HTMLAttributes {
-  max?: Numberish
-  value?: string | string[] | number
-}
-
-export interface ScriptHTMLAttributes extends HTMLAttributes {
-  async?: Booleanish
-  charset?: string
-  crossorigin?: string
-  defer?: Booleanish
-  integrity?: string
-  nomodule?: Booleanish
-  nonce?: string
-  src?: string
-  type?: string
-}
-
-export interface SelectHTMLAttributes extends HTMLAttributes {
-  autocomplete?: string
-  autofocus?: Booleanish
-  disabled?: Booleanish
-  form?: string
-  multiple?: Booleanish
-  name?: string
-  required?: Booleanish
-  size?: Numberish
-  value?: any // we support :value to be bound to anything w/ v-model
-}
-
-export interface SourceHTMLAttributes extends HTMLAttributes {
-  media?: string
-  sizes?: string
-  src?: string
-  srcset?: string
-  type?: string
-}
-
-export interface StyleHTMLAttributes extends HTMLAttributes {
-  media?: string
-  nonce?: string
-  scoped?: Booleanish
-  type?: string
-}
-
-export interface TableHTMLAttributes extends HTMLAttributes {
-  cellpadding?: Numberish
-  cellspacing?: Numberish
-  summary?: string
-}
-
-export interface TextareaHTMLAttributes extends HTMLAttributes {
-  autocomplete?: string
-  autofocus?: Booleanish
-  cols?: Numberish
-  dirname?: string
-  disabled?: Booleanish
-  form?: string
-  maxlength?: Numberish
-  minlength?: Numberish
-  name?: string
-  placeholder?: string
-  readonly?: boolean
-  required?: Booleanish
-  rows?: Numberish
-  value?: string | string[] | number
-  wrap?: string
-}
-
-export interface TdHTMLAttributes extends HTMLAttributes {
-  align?: 'left' | 'center' | 'right' | 'justify' | 'char'
-  colspan?: Numberish
-  headers?: string
-  rowspan?: Numberish
-  scope?: string
-  valign?: 'top' | 'middle' | 'bottom' | 'baseline'
-}
-
-export interface ThHTMLAttributes extends HTMLAttributes {
-  align?: 'left' | 'center' | 'right' | 'justify' | 'char'
-  colspan?: Numberish
-  headers?: string
-  rowspan?: Numberish
-  scope?: string
-}
-
-export interface TimeHTMLAttributes extends HTMLAttributes {
-  datetime?: string
-}
-
-export interface TrackHTMLAttributes extends HTMLAttributes {
-  default?: Booleanish
-  kind?: string
-  label?: string
-  src?: string
-  srclang?: string
-}
-
-export interface VideoHTMLAttributes extends MediaHTMLAttributes {
-  height?: Numberish
-  playsinline?: Booleanish
-  poster?: string
-  width?: Numberish
-  disablePictureInPicture?: Booleanish
-}
-
-export interface WebViewHTMLAttributes extends HTMLAttributes {
-  allowfullscreen?: Booleanish
-  allowpopups?: Booleanish
-  autoFocus?: Booleanish
-  autosize?: Booleanish
-  blinkfeatures?: string
-  disableblinkfeatures?: string
-  disableguestresize?: Booleanish
-  disablewebsecurity?: Booleanish
-  guestinstance?: string
-  httpreferrer?: string
-  nodeintegration?: Booleanish
-  partition?: string
-  plugins?: Booleanish
-  preload?: string
-  src?: string
-  useragent?: string
-  webpreferences?: string
-}
-
-export interface SVGAttributes extends AriaAttributes, EventHandlers<Events> {
-  innerHTML?: string
-
-  /**
-   * SVG Styling Attributes
-   * @see https://www.w3.org/TR/SVG/styling.html#ElementSpecificStyling
-   */
-  class?: any
-  style?: StyleValue
-
-  color?: string
-  height?: Numberish
-  id?: string
-  lang?: string
-  max?: Numberish
-  media?: string
-  method?: string
-  min?: Numberish
-  name?: string
-  target?: string
-  type?: string
-  width?: Numberish
-
-  // Other HTML properties supported by SVG elements in browsers
-  role?: string
-  tabindex?: Numberish
-
-  // SVG Specific attributes
-  'accent-height'?: Numberish
-  accumulate?: 'none' | 'sum'
-  additive?: 'replace' | 'sum'
-  'alignment-baseline'?:
-    | 'auto'
-    | 'baseline'
-    | 'before-edge'
-    | 'text-before-edge'
-    | 'middle'
-    | 'central'
-    | 'after-edge'
-    | 'text-after-edge'
-    | 'ideographic'
-    | 'alphabetic'
-    | 'hanging'
-    | 'mathematical'
-    | 'inherit'
-  allowReorder?: 'no' | 'yes'
-  alphabetic?: Numberish
-  amplitude?: Numberish
-  'arabic-form'?: 'initial' | 'medial' | 'terminal' | 'isolated'
-  ascent?: Numberish
-  attributeName?: string
-  attributeType?: string
-  autoReverse?: Numberish
-  azimuth?: Numberish
-  baseFrequency?: Numberish
-  'baseline-shift'?: Numberish
-  baseProfile?: Numberish
-  bbox?: Numberish
-  begin?: Numberish
-  bias?: Numberish
-  by?: Numberish
-  calcMode?: Numberish
-  'cap-height'?: Numberish
-  clip?: Numberish
-  'clip-path'?: string
-  clipPathUnits?: Numberish
-  'clip-rule'?: Numberish
-  'color-interpolation'?: Numberish
-  'color-interpolation-filters'?: 'auto' | 'sRGB' | 'linearRGB' | 'inherit'
-  'color-profile'?: Numberish
-  'color-rendering'?: Numberish
-  contentScriptType?: Numberish
-  contentStyleType?: Numberish
-  cursor?: Numberish
-  cx?: Numberish
-  cy?: Numberish
-  d?: string
-  decelerate?: Numberish
-  descent?: Numberish
-  diffuseConstant?: Numberish
-  direction?: Numberish
-  display?: Numberish
-  divisor?: Numberish
-  'dominant-baseline'?: Numberish
-  dur?: Numberish
-  dx?: Numberish
-  dy?: Numberish
-  edgeMode?: Numberish
-  elevation?: Numberish
-  'enable-background'?: Numberish
-  end?: Numberish
-  exponent?: Numberish
-  externalResourcesRequired?: Numberish
-  fill?: string
-  'fill-opacity'?: Numberish
-  'fill-rule'?: 'nonzero' | 'evenodd' | 'inherit'
-  filter?: string
-  filterRes?: Numberish
-  filterUnits?: Numberish
-  'flood-color'?: Numberish
-  'flood-opacity'?: Numberish
-  focusable?: Numberish
-  'font-family'?: string
-  'font-size'?: Numberish
-  'font-size-adjust'?: Numberish
-  'font-stretch'?: Numberish
-  'font-style'?: Numberish
-  'font-variant'?: Numberish
-  'font-weight'?: Numberish
-  format?: Numberish
-  from?: Numberish
-  fx?: Numberish
-  fy?: Numberish
-  g1?: Numberish
-  g2?: Numberish
-  'glyph-name'?: Numberish
-  'glyph-orientation-horizontal'?: Numberish
-  'glyph-orientation-vertical'?: Numberish
-  glyphRef?: Numberish
-  gradientTransform?: string
-  gradientUnits?: string
-  hanging?: Numberish
-  'horiz-adv-x'?: Numberish
-  'horiz-origin-x'?: Numberish
-  href?: string
-  ideographic?: Numberish
-  'image-rendering'?: Numberish
-  in2?: Numberish
-  in?: string
-  intercept?: Numberish
-  k1?: Numberish
-  k2?: Numberish
-  k3?: Numberish
-  k4?: Numberish
-  k?: Numberish
-  kernelMatrix?: Numberish
-  kernelUnitLength?: Numberish
-  kerning?: Numberish
-  keyPoints?: Numberish
-  keySplines?: Numberish
-  keyTimes?: Numberish
-  lengthAdjust?: Numberish
-  'letter-spacing'?: Numberish
-  'lighting-color'?: Numberish
-  limitingConeAngle?: Numberish
-  local?: Numberish
-  'marker-end'?: string
-  markerHeight?: Numberish
-  'marker-mid'?: string
-  'marker-start'?: string
-  markerUnits?: Numberish
-  markerWidth?: Numberish
-  mask?: string
-  maskContentUnits?: Numberish
-  maskUnits?: Numberish
-  mathematical?: Numberish
-  mode?: Numberish
-  numOctaves?: Numberish
-  offset?: Numberish
-  opacity?: Numberish
-  operator?: Numberish
-  order?: Numberish
-  orient?: Numberish
-  orientation?: Numberish
-  origin?: Numberish
-  overflow?: Numberish
-  'overline-position'?: Numberish
-  'overline-thickness'?: Numberish
-  'paint-order'?: Numberish
-  'panose-1'?: Numberish
-  pathLength?: Numberish
-  patternContentUnits?: string
-  patternTransform?: Numberish
-  patternUnits?: string
-  'pointer-events'?: Numberish
-  points?: string
-  pointsAtX?: Numberish
-  pointsAtY?: Numberish
-  pointsAtZ?: Numberish
-  preserveAlpha?: Numberish
-  preserveAspectRatio?: string
-  primitiveUnits?: Numberish
-  r?: Numberish
-  radius?: Numberish
-  refX?: Numberish
-  refY?: Numberish
-  renderingIntent?: Numberish
-  repeatCount?: Numberish
-  repeatDur?: Numberish
-  requiredExtensions?: Numberish
-  requiredFeatures?: Numberish
-  restart?: Numberish
-  result?: string
-  rotate?: Numberish
-  rx?: Numberish
-  ry?: Numberish
-  scale?: Numberish
-  seed?: Numberish
-  'shape-rendering'?: Numberish
-  slope?: Numberish
-  spacing?: Numberish
-  specularConstant?: Numberish
-  specularExponent?: Numberish
-  speed?: Numberish
-  spreadMethod?: string
-  startOffset?: Numberish
-  stdDeviation?: Numberish
-  stemh?: Numberish
-  stemv?: Numberish
-  stitchTiles?: Numberish
-  'stop-color'?: string
-  'stop-opacity'?: Numberish
-  'strikethrough-position'?: Numberish
-  'strikethrough-thickness'?: Numberish
-  string?: Numberish
-  stroke?: string
-  'stroke-dasharray'?: Numberish
-  'stroke-dashoffset'?: Numberish
-  'stroke-linecap'?: 'butt' | 'round' | 'square' | 'inherit'
-  'stroke-linejoin'?: 'miter' | 'round' | 'bevel' | 'inherit'
-  'stroke-miterlimit'?: Numberish
-  'stroke-opacity'?: Numberish
-  'stroke-width'?: Numberish
-  surfaceScale?: Numberish
-  systemLanguage?: Numberish
-  tableValues?: Numberish
-  targetX?: Numberish
-  targetY?: Numberish
-  'text-anchor'?: string
-  'text-decoration'?: Numberish
-  textLength?: Numberish
-  'text-rendering'?: Numberish
-  to?: Numberish
-  transform?: string
-  u1?: Numberish
-  u2?: Numberish
-  'underline-position'?: Numberish
-  'underline-thickness'?: Numberish
-  unicode?: Numberish
-  'unicode-bidi'?: Numberish
-  'unicode-range'?: Numberish
-  'unitsPer-em'?: Numberish
-  'v-alphabetic'?: Numberish
-  values?: string
-  'vector-effect'?: Numberish
-  version?: string
-  'vert-adv-y'?: Numberish
-  'vert-origin-x'?: Numberish
-  'vert-origin-y'?: Numberish
-  'v-hanging'?: Numberish
-  'v-ideographic'?: Numberish
-  viewBox?: string
-  viewTarget?: Numberish
-  visibility?: Numberish
-  'v-mathematical'?: Numberish
-  widths?: Numberish
-  'word-spacing'?: Numberish
-  'writing-mode'?: Numberish
-  x1?: Numberish
-  x2?: Numberish
-  x?: Numberish
-  xChannelSelector?: string
-  'x-height'?: Numberish
-  xlinkActuate?: string
-  xlinkArcrole?: string
-  xlinkHref?: string
-  xlinkRole?: string
-  xlinkShow?: string
-  xlinkTitle?: string
-  xlinkType?: string
-  xmlns?: string
-  y1?: Numberish
-  y2?: Numberish
-  y?: Numberish
-  yChannelSelector?: string
-  z?: Numberish
-  zoomAndPan?: string
-}
-
-interface IntrinsicElementAttributes {
-  a: AnchorHTMLAttributes
-  abbr: HTMLAttributes
-  address: HTMLAttributes
-  area: AreaHTMLAttributes
-  article: HTMLAttributes
-  aside: HTMLAttributes
-  audio: AudioHTMLAttributes
-  b: HTMLAttributes
-  base: BaseHTMLAttributes
-  bdi: HTMLAttributes
-  bdo: HTMLAttributes
-  blockquote: BlockquoteHTMLAttributes
-  body: HTMLAttributes
-  br: HTMLAttributes
-  button: ButtonHTMLAttributes
-  canvas: CanvasHTMLAttributes
-  caption: HTMLAttributes
-  cite: HTMLAttributes
-  code: HTMLAttributes
-  col: ColHTMLAttributes
-  colgroup: ColgroupHTMLAttributes
-  data: DataHTMLAttributes
-  datalist: HTMLAttributes
-  dd: HTMLAttributes
-  del: DelHTMLAttributes
-  details: DetailsHTMLAttributes
-  dfn: HTMLAttributes
-  dialog: DialogHTMLAttributes
-  div: HTMLAttributes
-  dl: HTMLAttributes
-  dt: HTMLAttributes
-  em: HTMLAttributes
-  embed: EmbedHTMLAttributes
-  fieldset: FieldsetHTMLAttributes
-  figcaption: HTMLAttributes
-  figure: HTMLAttributes
-  footer: HTMLAttributes
-  form: FormHTMLAttributes
-  h1: HTMLAttributes
-  h2: HTMLAttributes
-  h3: HTMLAttributes
-  h4: HTMLAttributes
-  h5: HTMLAttributes
-  h6: HTMLAttributes
-  head: HTMLAttributes
-  header: HTMLAttributes
-  hgroup: HTMLAttributes
-  hr: HTMLAttributes
-  html: HtmlHTMLAttributes
-  i: HTMLAttributes
-  iframe: IframeHTMLAttributes
-  img: ImgHTMLAttributes
-  input: InputHTMLAttributes
-  ins: InsHTMLAttributes
-  kbd: HTMLAttributes
-  keygen: KeygenHTMLAttributes
-  label: LabelHTMLAttributes
-  legend: HTMLAttributes
-  li: LiHTMLAttributes
-  link: LinkHTMLAttributes
-  main: HTMLAttributes
-  map: MapHTMLAttributes
-  mark: HTMLAttributes
-  menu: MenuHTMLAttributes
-  meta: MetaHTMLAttributes
-  meter: MeterHTMLAttributes
-  nav: HTMLAttributes
-  noindex: HTMLAttributes
-  noscript: HTMLAttributes
-  object: ObjectHTMLAttributes
-  ol: OlHTMLAttributes
-  optgroup: OptgroupHTMLAttributes
-  option: OptionHTMLAttributes
-  output: OutputHTMLAttributes
-  p: HTMLAttributes
-  param: ParamHTMLAttributes
-  picture: HTMLAttributes
-  pre: HTMLAttributes
-  progress: ProgressHTMLAttributes
-  q: QuoteHTMLAttributes
-  rp: HTMLAttributes
-  rt: HTMLAttributes
-  ruby: HTMLAttributes
-  s: HTMLAttributes
-  samp: HTMLAttributes
-  script: ScriptHTMLAttributes
-  section: HTMLAttributes
-  select: SelectHTMLAttributes
-  small: HTMLAttributes
-  source: SourceHTMLAttributes
-  span: HTMLAttributes
-  strong: HTMLAttributes
-  style: StyleHTMLAttributes
-  sub: HTMLAttributes
-  summary: HTMLAttributes
-  sup: HTMLAttributes
-  table: TableHTMLAttributes
-  template: HTMLAttributes
-  tbody: HTMLAttributes
-  td: TdHTMLAttributes
-  textarea: TextareaHTMLAttributes
-  tfoot: HTMLAttributes
-  th: ThHTMLAttributes
-  thead: HTMLAttributes
-  time: TimeHTMLAttributes
-  title: HTMLAttributes
-  tr: HTMLAttributes
-  track: TrackHTMLAttributes
-  u: HTMLAttributes
-  ul: HTMLAttributes
-  var: HTMLAttributes
-  video: VideoHTMLAttributes
-  wbr: HTMLAttributes
-  webview: WebViewHTMLAttributes
-
-  // SVG
-  svg: SVGAttributes
-
-  animate: SVGAttributes
-  animateMotion: SVGAttributes
-  animateTransform: SVGAttributes
-  circle: SVGAttributes
-  clipPath: SVGAttributes
-  defs: SVGAttributes
-  desc: SVGAttributes
-  ellipse: SVGAttributes
-  feBlend: SVGAttributes
-  feColorMatrix: SVGAttributes
-  feComponentTransfer: SVGAttributes
-  feComposite: SVGAttributes
-  feConvolveMatrix: SVGAttributes
-  feDiffuseLighting: SVGAttributes
-  feDisplacementMap: SVGAttributes
-  feDistantLight: SVGAttributes
-  feDropShadow: SVGAttributes
-  feFlood: SVGAttributes
-  feFuncA: SVGAttributes
-  feFuncB: SVGAttributes
-  feFuncG: SVGAttributes
-  feFuncR: SVGAttributes
-  feGaussianBlur: SVGAttributes
-  feImage: SVGAttributes
-  feMerge: SVGAttributes
-  feMergeNode: SVGAttributes
-  feMorphology: SVGAttributes
-  feOffset: SVGAttributes
-  fePointLight: SVGAttributes
-  feSpecularLighting: SVGAttributes
-  feSpotLight: SVGAttributes
-  feTile: SVGAttributes
-  feTurbulence: SVGAttributes
-  filter: SVGAttributes
-  foreignObject: SVGAttributes
-  g: SVGAttributes
-  image: SVGAttributes
-  line: SVGAttributes
-  linearGradient: SVGAttributes
-  marker: SVGAttributes
-  mask: SVGAttributes
-  metadata: SVGAttributes
-  mpath: SVGAttributes
-  path: SVGAttributes
-  pattern: SVGAttributes
-  polygon: SVGAttributes
-  polyline: SVGAttributes
-  radialGradient: SVGAttributes
-  rect: SVGAttributes
-  stop: SVGAttributes
-  switch: SVGAttributes
-  symbol: SVGAttributes
-  text: SVGAttributes
-  textPath: SVGAttributes
-  tspan: SVGAttributes
-  use: SVGAttributes
-  view: SVGAttributes
-}
-
-export interface Events {
-  // clipboard events
-  onCopy: ClipboardEvent
-  onCut: ClipboardEvent
-  onPaste: ClipboardEvent
-
-  // composition events
-  onCompositionend: CompositionEvent
-  onCompositionstart: CompositionEvent
-  onCompositionupdate: CompositionEvent
-
-  // drag drop events
-  onDrag: DragEvent
-  onDragend: DragEvent
-  onDragenter: DragEvent
-  onDragexit: DragEvent
-  onDragleave: DragEvent
-  onDragover: DragEvent
-  onDragstart: DragEvent
-  onDrop: DragEvent
-
-  // focus events
-  onFocus: FocusEvent
-  onFocusin: FocusEvent
-  onFocusout: FocusEvent
-  onBlur: FocusEvent
-
-  // form events
-  onChange: Event
-  onBeforeinput: Event
-  onInput: Event
-  onReset: Event
-  onSubmit: Event
-  onInvalid: Event
-
-  // image events
-  onLoad: Event
-  onError: Event
-
-  // keyboard events
-  onKeydown: KeyboardEvent
-  onKeypress: KeyboardEvent
-  onKeyup: KeyboardEvent
-
-  // mouse events
-  onAuxclick: MouseEvent
-  onClick: MouseEvent
-  onContextmenu: MouseEvent
-  onDblclick: MouseEvent
-  onMousedown: MouseEvent
-  onMouseenter: MouseEvent
-  onMouseleave: MouseEvent
-  onMousemove: MouseEvent
-  onMouseout: MouseEvent
-  onMouseover: MouseEvent
-  onMouseup: MouseEvent
-
-  // media events
-  onAbort: Event
-  onCanplay: Event
-  onCanplaythrough: Event
-  onDurationchange: Event
-  onEmptied: Event
-  onEncrypted: Event
-  onEnded: Event
-  onLoadeddata: Event
-  onLoadedmetadata: Event
-  onLoadstart: Event
-  onPause: Event
-  onPlay: Event
-  onPlaying: Event
-  onProgress: Event
-  onRatechange: Event
-  onSeeked: Event
-  onSeeking: Event
-  onStalled: Event
-  onSuspend: Event
-  onTimeupdate: Event
-  onVolumechange: Event
-  onWaiting: Event
-
-  // selection events
-  onSelect: Event
-
-  // UI events
-  onScroll: UIEvent
-
-  // touch events
-  onTouchcancel: TouchEvent
-  onTouchend: TouchEvent
-  onTouchmove: TouchEvent
-  onTouchstart: TouchEvent
-
-  // pointer events
-  onPointerdown: PointerEvent
-  onPointermove: PointerEvent
-  onPointerup: PointerEvent
-  onPointercancel: PointerEvent
-  onPointerenter: PointerEvent
-  onPointerleave: PointerEvent
-  onPointerover: PointerEvent
-  onPointerout: PointerEvent
-
-  // wheel events
-  onWheel: WheelEvent
-
-  // animation events
-  onAnimationstart: AnimationEvent
-  onAnimationend: AnimationEvent
-  onAnimationiteration: AnimationEvent
-
-  // transition events
-  onTransitionend: TransitionEvent
-  onTransitionstart: TransitionEvent
-}
-
-type EventHandlers<E> = {
-  [K in keyof E]?: E[K] extends (...args: any) => any
-    ? E[K]
-    : (payload: E[K]) => void
-}
-
-type ReservedProps = {
-  key?: string | number | symbol
-  ref?: VNodeData['ref']
-  /**
-   * @deprecated Old named slot syntax has been deprecated, use the new syntax
-   * instead: `<template v-slot:name>`
-   * https://v2.vuejs.org/v2/guide/components-slots.html#Named-Slots
-   */
-  slot?: string
-}
-
-type ElementAttrs<T> = T & ReservedProps
-
-type NativeElements = {
-  [K in keyof IntrinsicElementAttributes]: ElementAttrs<
-    IntrinsicElementAttributes[K]
-  >
-}
-
-import {
-  VNode,
-  VNodeData,
-  ComponentCustomProps,
-  AllowedComponentProps
-} from './vnode'
-
-declare global {
-  namespace JSX {
-    interface Element extends VNode {}
-    interface ElementClass {
-      $props: {}
-    }
-    interface ElementAttributesProperty {
-      $props: {}
-    }
-    interface IntrinsicElements extends NativeElements {
-      // allow arbitrary elements
-      // @ts-ignore suppress ts:2374 = Duplicate string index signature.
-      [name: string]: any
-    }
-    interface IntrinsicAttributes
-      extends ReservedProps,
-        AllowedComponentProps,
-        ComponentCustomProps {}
-  }
-}
-
-// suppress ts:2669
-export {}
diff --git a/types/options.d.ts b/types/options.d.ts
index 458bc9f2b94..8328e69ed59 100644
--- a/types/options.d.ts
+++ b/types/options.d.ts
@@ -1,349 +1,120 @@
-import { Vue, CreateElement, CombinedVueInstance } from './vue'
-import { VNode, VNodeData, VNodeDirective, NormalizedScopedSlot } from './vnode'
-import { SetupContext } from './v3-setup-context'
-import { DebuggerEvent } from './v3-generated'
-import { DefineComponent } from './v3-define-component'
-import { ComponentOptionsMixin } from './v3-component-options'
-import { ObjectDirective, FunctionDirective } from './v3-directive'
+import { Vue } from './vue';
 
 type Constructor = {
-  new (...args: any[]): any
-}
-
-// we don't support infer props in async component
-// N.B. ComponentOptions<V> is contravariant, the default generic should be bottom type
-export type Component<
-  Data = DefaultData<never>,
-  Methods = DefaultMethods<never>,
-  Computed = DefaultComputed,
-  Props = DefaultProps,
-  SetupBindings = {}
-> =
-  | typeof Vue
-  | FunctionalComponentOptions<Props>
-  | ComponentOptions<never, Data, Methods, Computed, Props, SetupBindings>
-  | DefineComponent<any, any, any, any, any, any, any, any, any, any, any>
-
-type EsModule<T> = T | { default: T }
-
-type ImportedComponent<
-  Data = DefaultData<never>,
-  Methods = DefaultMethods<never>,
-  Computed = DefaultComputed,
-  Props = DefaultProps,
-  SetupBindings = {}
-> = EsModule<Component<Data, Methods, Computed, Props, SetupBindings>>
+  new (...args: any[]): any;
+};
 
-export type AsyncComponent<
-  Data = DefaultData<never>,
-  Methods = DefaultMethods<never>,
-  Computed = DefaultComputed,
-  Props = DefaultProps,
-  SetupBindings = {}
-> =
-  | AsyncComponentPromise<Data, Methods, Computed, Props, SetupBindings>
-  | AsyncComponentFactory<Data, Methods, Computed, Props, SetupBindings>
+type Dictionary<T> = {
+  [key: string]: T;
+};
 
-export type AsyncComponentPromise<
-  Data = DefaultData<never>,
-  Methods = DefaultMethods<never>,
-  Computed = DefaultComputed,
-  Props = DefaultProps,
-  SetupBindings = {}
-> = (
-  resolve: (
-    component: Component<Data, Methods, Computed, Props, SetupBindings>
-  ) => void,
+export type Component = ComponentOptions<Vue> | typeof Vue;
+export type AsyncComponent = (
+  resolve: (component: Component) => void,
   reject: (reason?: any) => void
-) => Promise<
-  ImportedComponent<Data, Methods, Computed, Props, SetupBindings>
-> | void
-
-export type AsyncComponentFactory<
-  Data = DefaultData<never>,
-  Methods = DefaultMethods<never>,
-  Computed = DefaultComputed,
-  Props = DefaultProps,
-  SetupBindings = {}
-> = () => {
-  component: Promise<
-    ImportedComponent<Data, Methods, Computed, Props, SetupBindings>
-  >
-  loading?: ImportedComponent
-  error?: ImportedComponent
-  delay?: number
-  timeout?: number
+) => Promise<Component> | Component | void;
+
+export interface ComponentOptions<V extends Vue> {
+  data?: Dictionary<any> | ((this: V) => Dictionary<any>);
+  props?: string[] | Dictionary<PropOptions | Constructor | Constructor[]>;
+  propsData?: Dictionary<any>;
+  computed?: Dictionary<((this: V) => any) | ComputedOptions<V>>;
+  methods?: Dictionary<(this: V, ...args: any[]) => any>;
+  watch?: Dictionary<({ handler: WatchHandler<V> } & WatchOptions) | WatchHandler<V> | string>;
+
+  el?: string | HTMLElement | (() => HTMLElement);
+  template?: string;
+  replace?: boolean;
+
+  init?(this: V): void;
+  created?(this: V): void;
+  beforeCompile?(this: V): void;
+  compiled?(this: V): void;
+  activate?(this: V, done: () => void): void;
+  ready?(this: V): void;
+  attached?(this: V): void;
+  detached?(this: V): void;
+  beforeDestroy?(this: V): void;
+  destroyed?(this: V): void;
+
+  directives?: Dictionary<DirectiveOptions<V> | DirectiveFunction<V>>;
+  elementDirectives?: Dictionary<DirectiveOptions<V> | Function>;
+  filters?: Dictionary<Function | FilterOptions>;
+  components?: Dictionary<Component | AsyncComponent>;
+  transitions?: Dictionary<TransitionOptions>;
+  partials?: Dictionary<string>;
+
+  parent?: Vue;
+  events?: Dictionary<((...args: any[]) => (boolean | void)) | string>;
+  mixins?: (ComponentOptions<Vue> | typeof Vue)[];
+  name?: string;
 }
 
-/**
- * When the `Computed` type parameter on `ComponentOptions` is inferred,
- * it should have a property with the return type of every get-accessor.
- * Since there isn't a way to query for the return type of a function, we allow TypeScript
- * to infer from the shape of `Accessors<Computed>` and work backwards.
- */
-export type Accessors<T> = {
-  [K in keyof T]: (() => T[K]) | ComputedOptions<T[K]>
+export interface PropOptions {
+  type?: Constructor | Constructor[] | null;
+  required?: boolean;
+  default?: any;
+  twoWay?: boolean;
+  validator?(value: any): boolean;
+  coerce?(value: any): any;
 }
 
-type DataDef<Data, Props, V> = Data | ((this: Readonly<Props> & V) => Data)
-/**
- * This type should be used when an array of strings is used for a component's `props` value.
- */
-export type ThisTypedComponentOptionsWithArrayProps<
-  V extends Vue,
-  Data,
-  Methods,
-  Computed,
-  PropNames extends string,
-  SetupBindings,
-  Mixin extends ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin
-> = object &
-  ComponentOptions<
-    V,
-    DataDef<Data, Record<PropNames, any>, V>,
-    Methods,
-    Computed,
-    PropNames[],
-    Record<PropNames, any>,
-    SetupBindings,
-    Mixin,
-    Extends
-  > &
-  ThisType<
-    CombinedVueInstance<
-      V,
-      Data,
-      Methods,
-      Computed,
-      Readonly<Record<PropNames, any>>,
-      SetupBindings,
-      Mixin,
-      Extends
-    >
-  >
-
-/**
- * This type should be used when an object mapped to `PropOptions` is used for a component's `props` value.
- */
-export type ThisTypedComponentOptionsWithRecordProps<
-  V extends Vue,
-  Data,
-  Methods,
-  Computed,
-  Props,
-  SetupBindings,
-  Mixin extends ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin
-> = object &
-  ComponentOptions<
-    V,
-    DataDef<Data, Props, V>,
-    Methods,
-    Computed,
-    RecordPropsDefinition<Props>,
-    Props,
-    SetupBindings,
-    Mixin,
-    Extends
-  > &
-  ThisType<
-    CombinedVueInstance<
-      V,
-      Data,
-      Methods,
-      Computed,
-      Readonly<Props>,
-      SetupBindings,
-      Mixin,
-      Extends
-    >
-  >
-
-type DefaultData<V> = object | ((this: V) => object)
-type DefaultProps = Record<string, any>
-type DefaultMethods<V> = { [key: string]: (this: V, ...args: any[]) => any }
-type DefaultComputed = { [key: string]: any }
-
-export interface ComponentOptions<
-  V extends Vue,
-  Data = DefaultData<V>,
-  Methods = DefaultMethods<V>,
-  Computed = DefaultComputed,
-  PropsDef = PropsDefinition<DefaultProps>,
-  Props = DefaultProps,
-  RawBindings = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin
-> {
-  data?: Data
-  props?: PropsDef
-  propsData?: object
-  computed?: Accessors<Computed>
-  methods?: Methods
-  watch?: Record<string, WatchOptionsWithHandler<any> | WatchHandler<any> | Array<WatchOptionsWithHandler<any> | WatchHandler<any>>>
-
-  setup?: (
-    this: void,
-    props: Props,
-    ctx: SetupContext
-  ) => Promise<RawBindings> | RawBindings | ((h: CreateElement) => VNode) | void
-
-  el?: Element | string
-  template?: string
-  // hack is for functional component type inference, should not be used in user code
-  render?(
-    createElement: CreateElement,
-    hack: RenderContext<Props>
-  ): VNode | null | void
-  renderError?(createElement: CreateElement, err: Error): VNode
-  staticRenderFns?: ((createElement: CreateElement) => VNode)[]
-
-  beforeCreate?(this: V): void
-  created?(): void
-  beforeDestroy?(): void
-  destroyed?(): void
-  beforeMount?(): void
-  mounted?(): void
-  beforeUpdate?(): void
-  updated?(): void
-  activated?(): void
-  deactivated?(): void
-  errorCaptured?(err: Error, vm: Vue, info: string): boolean | void
-  serverPrefetch?(): Promise<void>
-  renderTracked?(e: DebuggerEvent): void
-  renderTriggerd?(e: DebuggerEvent): void
-
-  directives?: { [key: string]: DirectiveFunction | DirectiveOptions }
-  components?: {
-    [key: string]:
-      | {}
-      | Component<any, any, any, any, any>
-      | AsyncComponent<any, any, any, any>
-  }
-  transitions?: { [key: string]: object }
-  filters?: { [key: string]: Function }
-
-  provide?: object | (() => object)
-  inject?: InjectOptions
-
-  model?: {
-    prop?: string
-    event?: string
-  }
-
-  parent?: Vue
-  mixins?: (Mixin | ComponentOptions<Vue> | typeof Vue)[]
-  name?: string
-  // for SFC auto name inference w/ ts-loader check
-  __name?: string
-  // TODO: support properly inferred 'extends'
-  extends?: Extends | ComponentOptions<Vue> | typeof Vue
-  delimiters?: [string, string]
-  comments?: boolean
-  inheritAttrs?: boolean
+export interface ComputedOptions<V extends Vue> {
+  get?(this: V): any;
+  set(this: V, value: any): void;
 }
 
-export interface FunctionalComponentOptions<
-  Props = DefaultProps,
-  PropDefs = PropsDefinition<Props>
-> {
-  name?: string
-  props?: PropDefs
-  model?: {
-    prop?: string
-    event?: string
-  }
-  inject?: InjectOptions
-  functional: boolean
-  render?(
-    this: undefined,
-    createElement: CreateElement,
-    context: RenderContext<Props>
-  ): VNode | VNode[]
-}
-
-export interface RenderContext<Props = DefaultProps> {
-  props: Props
-  children: VNode[]
-  slots(): any
-  data: VNodeData
-  parent: Vue
-  listeners: { [key: string]: Function | Function[] }
-  scopedSlots: { [key: string]: NormalizedScopedSlot }
-  injections: any
-}
-
-export type Prop<T> =
-  | { (): T }
-  | { new (...args: never[]): T & object }
-  | { new (...args: string[]): Function }
-
-export type PropType<T> = Prop<T> | Prop<T>[]
-
-export type PropValidator<T> = PropOptions<T> | PropType<T>
-
-export interface PropOptions<T = any> {
-  type?: PropType<T>
-  required?: boolean
-  default?: T | null | undefined | (() => T | null | undefined)
-  validator?(value: unknown): boolean
-}
-
-export type RecordPropsDefinition<T> = {
-  [K in keyof T]: PropValidator<T[K]>
-}
-export type ArrayPropsDefinition<T> = (keyof T)[]
-export type PropsDefinition<T> =
-  | ArrayPropsDefinition<T>
-  | RecordPropsDefinition<T>
-
-export interface ComputedOptions<T> {
-  get?(): T
-  set?(value: T): void
-  cache?: boolean
-}
-
-export type WatchHandler<T> = string | ((val: T, oldVal: T) => void)
+export type WatchHandler<V> = (this: V, val: any, oldVal: any) => void;
 
 export interface WatchOptions {
-  deep?: boolean
-  immediate?: boolean
+  deep?: boolean;
+  immediate?: boolean;
 }
 
-export interface WatchOptionsWithHandler<T> extends WatchOptions {
-  handler: WatchHandler<T>
+export interface DirectiveInstance<V extends Vue> {
+  el: HTMLElement;
+  vm: V;
+  expression: string;
+  arg?: string;
+  name: string;
+  modifiers: Dictionary<boolean>;
+  descriptor: any;
+  params?: Dictionary<any>;
 }
 
-export interface DirectiveBinding extends Readonly<VNodeDirective> {
-  readonly modifiers: { [key: string]: boolean }
+export type DirectiveFunction<V extends Vue> = (this: DirectiveInstance<V>, newVal: any, oldVal: any) => void;
+
+export interface DirectiveOptions<V extends Vue> {
+  bind?(this: DirectiveInstance<V>): void;
+  update?(this: DirectiveInstance<V>, newVal: any, oldVal: any): void;
+  unbind?(this: DirectiveInstance<V>): void;
+  params?: string[];
+  deep?: boolean;
+  twoWay?: boolean;
+  acceptStatement?: boolean;
+  terminal?: boolean;
+  priority?: number;
 }
 
-/**
- * @deprecated use {@link FunctionDirective} instead
- */
-export type DirectiveFunction = (
-  el: HTMLElement,
-  binding: DirectiveBinding,
-  vnode: VNode,
-  oldVnode: VNode
-) => void
-
-/**
- * @deprecated use {@link ObjectDirective} instead
- */
-export interface DirectiveOptions {
-  bind?: DirectiveFunction
-  inserted?: DirectiveFunction
-  update?: DirectiveFunction
-  componentUpdated?: DirectiveFunction
-  unbind?: DirectiveFunction
+export interface FilterOptions {
+  read?: Function;
+  write?: Function;
 }
 
-export type InjectKey = string | symbol
-
-export type InjectOptions =
-  | {
-      [key: string]: InjectKey | { from?: InjectKey; default?: any }
-    }
-  | string[]
+export interface TransitionOptions {
+  css?: boolean;
+  animation?: string;
+  enterClass?: string;
+  leaveClass?: string;
+  beforeEnter?(el: HTMLElement): void;
+  enter?(el: HTMLElement, done: () => void): void;
+  afterEnter?(el: HTMLElement): void;
+  enterCancelled?(el: HTMLElement): void;
+  beforeLeave?(el: HTMLElement): void;
+  leave?(el: HTMLElement, done: () => void): void;
+  afterLeave?(el: HTMLElement): void;
+  leaveCancelled?(el: HTMLElement): void;
+  stagger?(index: number): number;
+  enterStagger?(index: number): number;
+  leaveStagger?(index: number): number;
+}
diff --git a/types/plugin.d.ts b/types/plugin.d.ts
index 8ebdfae9057..9061db96737 100644
--- a/types/plugin.d.ts
+++ b/types/plugin.d.ts
@@ -1,8 +1,10 @@
-import { Vue as _Vue } from './vue'
+import { Vue as _Vue } from './vue';
 
-export type PluginFunction<T> = (Vue: typeof _Vue, options?: T) => void
+export type PluginFunction<T> = (Vue: typeof _Vue, options?: T) => void;
 
 export interface PluginObject<T> {
-  install: PluginFunction<T>
-  [key: string]: any
+  install: PluginFunction<T>;
+  [key: string]: any;
 }
+
+export type Plugin<T> = PluginFunction<T> | PluginObject<T>;
diff --git a/types/test/async-component-test.ts b/types/test/async-component-test.ts
deleted file mode 100644
index 79978ddf8de..00000000000
--- a/types/test/async-component-test.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import Vue, { AsyncComponent, Component } from '../index'
-
-const a: AsyncComponent = () => ({
-  component: new Promise<Component>((res, rej) => {
-    res({ template: '' })
-  })
-})
-
-const b: AsyncComponent = () => ({
-  // @ts-expect-error component has to be a Promise that resolves to a component
-  component: () =>
-    new Promise<Component>((res, rej) => {
-      res({ template: '' })
-    })
-})
-
-const c: AsyncComponent = () =>
-  new Promise<Component>((res, rej) => {
-    res({
-      template: ''
-    })
-  })
-
-const d: AsyncComponent = () =>
-  new Promise<{ default: Component }>((res, rej) => {
-    res({
-      default: {
-        template: ''
-      }
-    })
-  })
-
-const e: AsyncComponent = () => ({
-  component: new Promise<{ default: Component }>((res, rej) => {
-    res({
-      default: {
-        template: ''
-      }
-    })
-  })
-})
-
-// Test that Vue.component accepts any AsyncComponent
-Vue.component('async-component1', a)
diff --git a/types/test/augmentation-test.ts b/types/test/augmentation-test.ts
index 6e165857893..dd569052be4 100644
--- a/types/test/augmentation-test.ts
+++ b/types/test/augmentation-test.ts
@@ -1,53 +1,35 @@
-import Vue, { defineComponent } from '../index'
+import Vue = require("../index");
 
-declare module '../vue' {
+declare module "../vue" {
   // add instance property and method
   interface Vue {
-    $instanceProperty: string
-    $instanceMethod(): void
+    $instanceProperty: string;
+    $instanceMethod(): void;
   }
 
   // add static property and method
-  interface VueConstructor {
-    staticProperty: string
-    staticMethod(): void
+  namespace Vue {
+    const staticProperty: string;
+    function staticMethod(): void;
   }
 }
 
 // augment ComponentOptions
-declare module '../options' {
+declare module "../options" {
   interface ComponentOptions<V extends Vue> {
-    foo?: string
+    foo?: string;
   }
 }
 
 const vm = new Vue({
-  props: ['bar'],
   data: {
     a: true
   },
-  foo: 'foo',
-  methods: {
-    foo() {
-      this.a = false
-    }
-  },
-  computed: {
-    BAR(): string {
-      return this.bar.toUpperCase()
-    }
-  }
-})
+  foo: "foo"
+});
 
-vm.$instanceProperty
-vm.$instanceMethod()
+vm.$instanceProperty;
+vm.$instanceMethod();
 
-Vue.staticProperty
-Vue.staticMethod()
-
-defineComponent({
-  mounted() {
-    this.$instanceMethod
-    this.$instanceProperty
-  }
-})
+Vue.staticProperty;
+Vue.staticMethod();
diff --git a/types/test/es-module.ts b/types/test/es-module.ts
deleted file mode 100644
index f124374df4f..00000000000
--- a/types/test/es-module.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export default {
-  data() {
-    return {}
-  }
-}
diff --git a/types/test/global-test.ts b/types/test/global-test.ts
new file mode 100644
index 00000000000..f06cda37b81
--- /dev/null
+++ b/types/test/global-test.ts
@@ -0,0 +1,17 @@
+declare var Vue: vuejs.VueStatic;
+
+var app = new Vue({
+  data: {
+    message: ""
+  }
+});
+
+app.$mount("#app");
+
+class Application extends Vue {}
+
+Vue.component("component", {
+  ready () {
+    this.a
+  }
+} as vuejs.ComponentOption)
diff --git a/types/test/options-test.ts b/types/test/options-test.ts
index 8b724083709..41af4868657 100644
--- a/types/test/options-test.ts
+++ b/types/test/options-test.ts
@@ -1,126 +1,14 @@
-import Vue, { PropType, VNode } from '../index'
-import { ComponentOptions, Component } from '../index'
-import { CreateElement } from '../vue'
+import Vue = require("../index");
+import { ComponentOptions } from "../index";
 
-interface MyComponent extends Vue {
-  a: number
+interface Component extends Vue {
+  a: number;
 }
 
-const option: ComponentOptions<MyComponent> = {
-  data() {
-    return {
-      a: 123
-    }
-  }
-}
-
-// contravariant generic should use never
-const anotherOption: ComponentOptions<never> = option
-const componentType: Component = option
-
-Vue.component('sub-component', {
-  components: {
-    a: Vue.component(''),
-    b: {}
-  }
-})
-
-Vue.component('prop-component', {
-  props: {
-    size: Number,
-    name: {
-      type: String,
-      default: '0',
-      required: true
-    }
-  },
-  data() {
-    return {
-      fixedSize: this.size.toFixed(),
-      capName: this.name.toUpperCase()
-    }
-  }
-})
-
-Vue.component('string-prop', {
-  props: ['size', 'name'],
-  data() {
-    return {
-      fixedSize: this.size.whatever,
-      capName: this.name.isany
-    }
-  }
-})
-
-class User {
-  private u = 1
-}
-class Cat {
-  private u = 1
-}
-
-interface IUser {
-  foo: string
-  bar: number
-}
-
-interface ICat {
-  foo: any
-  bar: object
-}
-type ConfirmCallback = (confirm: boolean) => void
-
-Vue.component('union-prop', {
-  props: {
-    cat: Object as PropType<ICat>,
-    complexUnion: { type: [User, Number] as PropType<User | number> },
-    kittyUser: Object as PropType<ICat & IUser>,
-    callback: Function as PropType<ConfirmCallback>,
-    union: [User, Number] as PropType<User | number>
-  },
-  data() {
-    this.cat
-    this.complexUnion
-    this.kittyUser
-    this.callback(true)
-    this.union
-    return {
-      fixedSize: this.union
-    }
-  }
-})
-
-Vue.component('union-prop-with-no-casting', {
-  props: {
-    mixed: [RegExp, Array],
-    object: [Cat, User],
-    primitive: [String, Number],
-    regex: RegExp
-  },
-  data() {
-    this.mixed
-    this.object
-    this.primitive
-    this.regex.compile
-  }
-})
-
-Vue.component('prop-with-primitive-default', {
-  props: {
-    id: {
-      type: String,
-      default: () => String(Math.round(Math.random() * 10000000))
-    }
-  },
-  created(): void {
-    this.id
-  }
-})
-
 Vue.component('component', {
   data() {
     this.$mount
-    this.size
+    this.a
     return {
       a: 1
     }
@@ -129,129 +17,52 @@ Vue.component('component', {
     size: Number,
     name: {
       type: String,
-      default: '0',
-      required: true
+      default: 0,
+      required: true,
+      validator(value) {
+        return value > 0;
+      }
     }
   },
   propsData: {
-    msg: 'Hello'
+    msg: "Hello"
   },
   computed: {
-    aDouble(): number {
-      return this.a * 2
+    aDouble(this: Component) {
+      return this.a * 2;
     },
     aPlus: {
-      get(): number {
-        return this.a + 1
+      get(this: Component) {
+        return this.a + 1;
       },
-      set(v: number) {
-        this.a = v - 1
+      set(this: Component, v: number) {
+        this.a = v - 1;
       },
       cache: false
     }
   },
   methods: {
-    plus(): void {
-      this.a++
-      this.aDouble.toFixed()
-      this.aPlus = 1
-      this.size.toFixed()
+    plus() {
+      this.a++;
     }
   },
   watch: {
-    a: function (val: number, oldVal: number) {
-      console.log(`new: ${val}, old: ${oldVal}`)
+    'a': function(val: number, oldVal: number) {
+      console.log(`new: ${val}, old: ${oldVal}`);
     },
-    b: 'someMethod',
-    c: {
+    'b': 'someMethod',
+    'c': {
       handler(val, oldVal) {
         this.a = val
       },
       deep: true
-    },
-    d: {
-      handler: 'someMethod',
-      immediate: true
-    },
-    e: [
-      'handle1',
-      function handle2 (val, oldVal) {},
-      {
-        handler: function handle3 (val, oldVal) {},
-      }
-    ],
-  },
-  el: '#app',
-  template: '<div>{{ message }}</div>',
-  render(createElement) {
-    return createElement(
-      'div',
-      {
-        attrs: {
-          id: 'foo'
-        },
-        props: {
-          myProp: 'bar'
-        },
-        directives: [
-          {
-            name: 'a',
-            value: 'foo'
-          }
-        ],
-        domProps: {
-          innerHTML: 'baz'
-        },
-        on: {
-          click: new Function()
-        },
-        nativeOn: {
-          click: new Function()
-        },
-        class: {
-          foo: true,
-          bar: false
-        },
-        style: {
-          color: 'red',
-          fontSize: '14px'
-        },
-        key: 'myKey',
-        ref: 'myRef',
-        refInFor: true
-      },
-      [
-        createElement(),
-        createElement('div', 'message'),
-        createElement(Vue.component('component')),
-        createElement({} as ComponentOptions<Vue>),
-        createElement({
-          functional: true,
-          render(c: CreateElement) {
-            return createElement()
-          }
-        }),
-
-        createElement(() => Vue.component('component')),
-        createElement(() => ({} as ComponentOptions<Vue>)),
-        createElement((resolve, reject) => {
-          resolve({} as ComponentOptions<Vue>)
-          reject()
-        }),
-
-        'message',
-
-        [createElement('div', 'message')]
-      ]
-    )
-  },
-  renderError(createElement, err) {
-    return createElement('pre', { style: { color: 'red' } }, err.stack)
+    }
   },
-  staticRenderFns: [],
+  el: "#app",
+  template: "<div>{{ message }}</div>",
 
   beforeCreate() {
-    ;(this as any).a = 1
+    this.a = 1;
   },
   created() {},
   beforeDestroy() {},
@@ -262,255 +73,46 @@ Vue.component('component', {
   updated() {},
   activated() {},
   deactivated() {},
-  errorCaptured(err, vm, info) {
-    err.message
-    vm.$emit('error')
-    info.toUpperCase()
-    return true
-  },
-  serverPrefetch() {
-    return Promise.resolve()
-  },
 
   directives: {
     a: {
       bind() {},
       inserted() {},
       update() {},
-      componentUpdated() {},
+      componentMounted() {},
       unbind() {}
     },
-    b(el, binding, vnode, oldVnode) {
-      el.textContent
+    b(val, newVal) {
+      this.el.textContent;
 
-      binding.name
-      binding.value
-      binding.oldValue
-      binding.expression
-      binding.arg
-      binding.modifiers['modifier']
+      this.name;
+      this.expression;
+      this.arg;
+      this.modifiers["modifier"];
     }
   },
   components: {
-    a: Vue.component(''),
+    a: Vue.component(""),
     b: {} as ComponentOptions<Vue>
   },
   transitions: {},
   filters: {
     double(value: number) {
-      return value * 2
+      return value * 2;
     }
   },
-  parent: new Vue(),
-  mixins: [Vue.component(''), {} as ComponentOptions<Vue>],
-  name: 'Component',
+  parent: new Vue,
+  mixins: [Vue.component(""), ({} as ComponentOptions<Vue>)],
+  name: "Component",
   extends: {} as ComponentOptions<Vue>,
-  delimiters: ['${', '}']
-})
-
-Vue.component('custom-prop-type-function', {
-  props: {
-    callback: Function as PropType<(confirm: boolean) => void>
-  },
-  methods: {
-    confirm() {
-      this.callback(true)
-    }
-  }
-})
-
-Vue.component('provide-inject', {
-  provide: {
-    foo: 1
-  },
-  inject: {
-    injectFoo: 'foo',
-    injectBar: Symbol(),
-    injectBaz: { from: 'baz' },
-    injectQux: { default: 1 },
-    injectQuux: { from: 'quuz', default: () => ({ value: 1 }) }
-  }
-})
-
-Vue.component('provide-function', {
-  provide: () => ({
-    foo: 1
-  })
-})
-
-Vue.component('component-with-slot', {
-  render(h): VNode {
-    return h('div', this.$slots.default)
-  }
-})
+  delimiters: ["${", "}"]
+} as ComponentOptions<Component>);
 
-Vue.component('component-with-scoped-slot', {
-  render(h) {
-    interface ScopedSlotProps {
-      msg: string
-    }
-
-    return h('div', [
-      h('child', [
-        // default scoped slot as children
-        (props: ScopedSlotProps) => [h('span', [props.msg])]
-      ]),
-      h('child', {
-        scopedSlots: {
-          // named scoped slot as vnode data
-          item: (props: ScopedSlotProps) => [h('span', [props.msg])]
-        }
-      }),
-      h('child', [
-        // return single VNode (will be normalized to an array)
-        (props: ScopedSlotProps) => h('span', [props.msg])
-      ]),
-      h('child', {
-        // Passing down all slots from parent
-        scopedSlots: this.$scopedSlots
-      }),
-      h('child', {
-        // Passing down single slot from parent
-        scopedSlots: {
-          default: this.$scopedSlots.default
-        }
-      })
-    ])
-  },
-  components: {
-    child: {
-      render(this: Vue, h: CreateElement) {
-        const defaultSlot = this.$scopedSlots['default']!({ msg: 'hi' })
-        defaultSlot &&
-          defaultSlot.forEach(vnode => {
-            vnode.tag
-          })
-        return h('div', [
-          defaultSlot,
-          this.$scopedSlots['item']!({ msg: 'hello' })
-        ])
-      }
-    }
-  }
-})
-
-Vue.component('narrow-array-of-vnode-type', {
-  render(h): VNode {
-    const slot = this.$scopedSlots.default!({})
-    if (typeof slot === 'string') {
-      // <template slot-scope="data">bare string</template>
-      return h('span', slot)
-    } else if (Array.isArray(slot)) {
-      // template with multiple children
-      const first = slot[0]
-      if (!Array.isArray(first) && typeof first !== 'string' && first) {
-        return first
-      } else {
-        return h()
-      }
-    } else if (slot) {
-      // <div slot-scope="data">bare VNode</div>
-      return slot
-    } else {
-      // empty template, slot === undefined
-      return h()
-    }
-  }
-})
-
-Vue.component('functional-component', {
-  props: ['prop'],
-  functional: true,
-  inject: ['foo'],
-  render(createElement, context) {
-    context.props
-    context.children
-    context.slots()
-    context.data
-    context.parent
-    context.scopedSlots
-    context.listeners.click
-    return createElement('div', {}, context.children)
-  }
-})
-
-Vue.component('functional-component-object-inject', {
-  functional: true,
-  inject: {
-    foo: 'foo',
-    bar: Symbol(),
-    baz: { from: 'baz' },
-    qux: { default: 1 },
-    quux: { from: 'quuz', default: () => ({ value: 1 }) }
-  },
-  render(h) {
-    return h('div')
-  }
-})
-
-Vue.component('functional-component-check-optional', {
-  functional: true
-})
-
-Vue.component('functional-component-multi-root', {
-  functional: true,
-  render(h) {
-    return [
-      h('tr', [h('td', 'foo'), h('td', 'bar')]),
-      h('tr', [h('td', 'lorem'), h('td', 'ipsum')])
-    ]
-  }
-})
-
-Vue.component('async-component', (resolve, reject) => {
+Vue.component("async-component", (resolve, reject) => {
   setTimeout(() => {
-    resolve(Vue.component('component'))
-  }, 0)
-  return new Promise(resolve => {
-    resolve({
-      functional: true,
-      render(h: CreateElement) {
-        return h('div')
-      }
-    })
+    resolve(Vue.component("component"));
+  }, 0);
+  return new Promise((resolve) => {
+    resolve({ } as ComponentOptions<Vue>);
   })
-})
-
-Vue.component('functional-component-v-model', {
-  props: ['foo'],
-  functional: true,
-  model: {
-    prop: 'foo',
-    event: 'change'
-  },
-  render(createElement, context) {
-    return createElement('input', {
-      on: {
-        input: new Function()
-      },
-      domProps: {
-        value: context.props.foo
-      }
-    })
-  }
-})
-
-Vue.component('async-es-module-component', () => import('./es-module'))
-
-Vue.component('directive-expression-optional-string', {
-  render(createElement) {
-    return createElement('div', {
-      directives: [
-        {
-          name: 'has-expression',
-          value: 2,
-          expression: '1 + 1'
-        },
-        {
-          name: 'no-expression',
-          value: 'foo'
-        }
-      ]
-    })
-  }
-})
+});
diff --git a/types/test/plugin-test.ts b/types/test/plugin-test.ts
index c6c480343f3..eb1a0d12afa 100644
--- a/types/test/plugin-test.ts
+++ b/types/test/plugin-test.ts
@@ -1,20 +1,19 @@
-import Vue from '../index'
-import { PluginFunction, PluginObject } from '../index'
+import Vue = require("../index");
+import { PluginFunction, PluginObject } from "../index";
 
 class Option {
-  prefix: string = ''
-  suffix: string = ''
+  prefix: string;
+  suffix: string;
 }
 
 const plugin: PluginObject<Option> = {
   install(Vue, option) {
-    if (typeof option !== 'undefined') {
-      const { prefix, suffix } = option
+    if (typeof option !== "undefined") {
+      const {prefix, suffix} = option;
     }
   }
 }
-const installer: PluginFunction<Option> = function (Vue, option) {}
+const installer: PluginFunction<Option> = function(Vue, option) { }
 
-Vue.use(plugin, new Option())
-Vue.use(installer, new Option())
-Vue.use(installer, new Option(), new Option(), new Option())
+Vue.use(plugin, new Option);
+Vue.use(installer, new Option);
diff --git a/types/test/setup-helpers-test.ts b/types/test/setup-helpers-test.ts
deleted file mode 100644
index 4876154b67d..00000000000
--- a/types/test/setup-helpers-test.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-import { useAttrs, useSlots, SetupContext } from '../index'
-import { describe, expectType } from './utils'
-
-describe('defineProps w/ type declaration', () => {
-  // type declaration
-  const props = defineProps<{
-    foo: string
-  }>()
-  // explicitly declared type should be refined
-  expectType<string>(props.foo)
-  // @ts-expect-error
-  props.bar
-})
-
-describe('defineProps w/ type declaration + withDefaults', () => {
-  const res = withDefaults(
-    defineProps<{
-      number?: number
-      arr?: string[]
-      obj?: { x: number }
-      fn?: (e: string) => void
-      genStr?: string
-      x?: string
-      y?: string
-      z?: string
-    }>(),
-    {
-      number: 123,
-      arr: () => [],
-      obj: () => ({ x: 123 }),
-      fn: () => {},
-      genStr: () => '',
-      y: undefined,
-      z: 'string'
-    }
-  )
-
-  res.number + 1
-  res.arr.push('hi')
-  res.obj.x
-  res.fn('hi')
-  res.genStr.slice()
-  // @ts-expect-error
-  res.x.slice()
-  // @ts-expect-error
-  res.y.slice()
-
-  expectType<string | undefined>(res.x)
-  expectType<string | undefined>(res.y)
-  expectType<string>(res.z)
-})
-
-describe('defineProps w/ union type declaration + withDefaults', () => {
-  withDefaults(
-    defineProps<{
-      union1?: number | number[] | { x: number }
-      union2?: number | number[] | { x: number }
-      union3?: number | number[] | { x: number }
-      union4?: number | number[] | { x: number }
-    }>(),
-    {
-      union1: 123,
-      union2: () => [123],
-      union3: () => ({ x: 123 }),
-      union4: () => 123
-    }
-  )
-})
-
-describe('defineProps w/ runtime declaration', () => {
-  // runtime declaration
-  const props = defineProps({
-    foo: String,
-    bar: {
-      type: Number,
-      default: 1
-    },
-    baz: {
-      type: Array,
-      required: true
-    }
-  })
-  expectType<{
-    foo?: string
-    bar: number
-    baz: unknown[]
-  }>(props)
-
-  props.foo && props.foo + 'bar'
-  props.bar + 1
-  // @ts-expect-error should be readonly
-  props.bar++
-  props.baz.push(1)
-
-  const props2 = defineProps(['foo', 'bar'])
-  props2.foo + props2.bar
-  // @ts-expect-error
-  props2.baz
-})
-
-describe('defineEmits w/ type declaration', () => {
-  const emit = defineEmits<(e: 'change') => void>()
-  emit('change')
-  // @ts-expect-error
-  emit()
-  // @ts-expect-error
-  emit('bar')
-
-  type Emits = { (e: 'foo' | 'bar'): void; (e: 'baz', id: number): void }
-  const emit2 = defineEmits<Emits>()
-
-  emit2('foo')
-  emit2('bar')
-  emit2('baz', 123)
-  // @ts-expect-error
-  emit2('baz')
-})
-
-describe('defineEmits w/ runtime declaration', () => {
-  const emit = defineEmits({
-    foo: () => {},
-    bar: null
-  })
-  emit('foo')
-  emit('bar', 123)
-  // @ts-expect-error
-  emit('baz')
-
-  const emit2 = defineEmits(['foo', 'bar'])
-  emit2('foo')
-  emit2('bar', 123)
-  // @ts-expect-error
-  emit2('baz')
-})
-
-describe('useAttrs', () => {
-  const attrs = useAttrs()
-  expectType<Record<string, unknown>>(attrs)
-})
-
-describe('useSlots', () => {
-  const slots = useSlots()
-  expectType<SetupContext['slots']>(slots)
-})
diff --git a/types/test/tsconfig.json b/types/test/tsconfig.json
new file mode 100644
index 00000000000..1f5ae14fd53
--- /dev/null
+++ b/types/test/tsconfig.json
@@ -0,0 +1,26 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "lib": [
+      "es5",
+      "dom",
+      "es2015.promise"
+    ],
+    "module": "commonjs",
+    "noImplicitAny": true,
+    "strictNullChecks": true,
+    "noEmit": true
+  },
+  "files": [
+    "../index.d.ts",
+    "../options.d.ts",
+    "../plugin.d.ts",
+    "../vue.d.ts",
+    "options-test.ts",
+    "plugin-test.ts",
+    "vue-test.ts",
+    "augmentation-test.ts",
+    "global-test.ts"
+  ],
+  "compileOnSave": false
+}
diff --git a/types/test/umd-test.ts b/types/test/umd-test.ts
deleted file mode 100644
index d8e05b88fbf..00000000000
--- a/types/test/umd-test.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-const vm = new Vue({
-  template: '<div>hi</div>'
-})
-
-const options: Vue.ComponentOptions<Vue> = {
-  template: '<div>test</div>'
-}
diff --git a/types/test/utils.ts b/types/test/utils.ts
deleted file mode 100644
index 48c4fa882df..00000000000
--- a/types/test/utils.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-export declare function describe(_name: string, _fn: () => void): void
-export declare function test(_name: string, _fn: () => any): void
-
-export declare function expectType<T>(value: T): void
-export declare function expectError<T>(value: T): void
-export declare function expectAssignable<T, T2 extends T = T>(value: T2): void
-
-export type IsUnion<T, U extends T = T> = (
-  T extends any ? (U extends T ? false : true) : never
-) extends false
-  ? false
-  : true
-
-export type IsAny<T> = 0 extends 1 & T ? true : false
diff --git a/types/test/v3/define-async-component-test.tsx b/types/test/v3/define-async-component-test.tsx
deleted file mode 100644
index f4fbe4ba452..00000000000
--- a/types/test/v3/define-async-component-test.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { defineAsyncComponent } from '../../v3-define-async-component'
-import { defineComponent } from '../../v3-define-component'
-
-defineAsyncComponent(() => Promise.resolve({}))
-
-// @ts-expect-error
-defineAsyncComponent({})
-
-defineAsyncComponent({
-  loader: () => Promise.resolve({}),
-  loadingComponent: defineComponent({}),
-  errorComponent: defineComponent({}),
-  delay: 123,
-  timeout: 3000,
-  onError(err, retry, fail, attempts) {
-    retry()
-    fail()
-  }
-})
diff --git a/types/test/v3/define-component-test.tsx b/types/test/v3/define-component-test.tsx
deleted file mode 100644
index 7e6d1968ca3..00000000000
--- a/types/test/v3/define-component-test.tsx
+++ /dev/null
@@ -1,1227 +0,0 @@
-import Vue, { VueConstructor } from '../../index'
-import {
-  Component,
-  defineComponent,
-  PropType,
-  ref,
-  reactive,
-  ComponentPublicInstance
-} from '../../index'
-import { describe, test, expectType, expectError, IsUnion } from '../utils'
-
-describe('compat with v2 APIs', () => {
-  const comp = defineComponent({})
-
-  Vue.component('foo', comp)
-  function install(app: VueConstructor) {
-    app.component('foo', comp)
-  }
-})
-
-describe('with object props', () => {
-  interface ExpectedProps {
-    a?: number | undefined
-    b: string
-    e?: Function
-    h: boolean
-    j: undefined | (() => string | undefined)
-    bb: string
-    bbb: string
-    bbbb: string | undefined
-    bbbbb: string | undefined
-    cc?: string[] | undefined
-    dd: { n: 1 }
-    ee?: () => string
-    ff?: (a: number, b: string) => { a: boolean }
-    ccc?: string[] | undefined
-    ddd: string[]
-    eee: () => { a: string }
-    fff: (a: number, b: string) => { a: boolean }
-    hhh: boolean
-    ggg: 'foo' | 'bar'
-    ffff: (a: number, b: string) => { a: boolean }
-    iii?: (() => string) | (() => number)
-    jjj: ((arg1: string) => string) | ((arg1: string, arg2: string) => string)
-    kkk?: any
-    validated?: string
-    date?: Date
-    l?: Date
-    ll?: Date | number
-    lll?: string | number
-  }
-
-  type GT = string & { __brand: unknown }
-
-  const props = {
-    a: Number,
-    // required should make property non-void
-    b: {
-      type: String,
-      required: true as true
-    },
-    e: Function,
-    h: Boolean,
-    j: Function as PropType<undefined | (() => string | undefined)>,
-    // default value should infer type and make it non-void
-    bb: {
-      default: 'hello'
-    },
-    bbb: {
-      // Note: default function value requires arrow syntax + explicit
-      // annotation
-      default: (props: any) => (props.bb as string) || 'foo'
-    },
-    bbbb: {
-      type: String,
-      default: undefined
-    },
-    bbbbb: {
-      type: String,
-      default: () => undefined
-    },
-    // explicit type casting
-    cc: Array as PropType<string[]>,
-    // required + type casting
-    dd: {
-      type: Object as PropType<{ n: 1 }>,
-      required: true as true
-    },
-    // return type
-    ee: Function as PropType<() => string>,
-    // arguments + object return
-    ff: Function as PropType<(a: number, b: string) => { a: boolean }>,
-    // explicit type casting with constructor
-    ccc: Array as () => string[],
-    // required + constructor type casting
-    ddd: {
-      type: Array as () => string[],
-      required: true as true
-    },
-    // required + object return
-    eee: {
-      type: Function as PropType<() => { a: string }>,
-      required: true as true
-    },
-    // required + arguments + object return
-    fff: {
-      type: Function as PropType<(a: number, b: string) => { a: boolean }>,
-      required: true as true
-    },
-    hhh: {
-      type: Boolean,
-      required: true as true
-    },
-    // default + type casting
-    ggg: {
-      type: String as PropType<'foo' | 'bar'>,
-      default: 'foo'
-    },
-    // default + function
-    ffff: {
-      type: Function as PropType<(a: number, b: string) => { a: boolean }>,
-      default: (a: number, b: string) => ({ a: a > +b })
-    },
-    // union + function with different return types
-    iii: Function as PropType<(() => string) | (() => number)>,
-    // union + function with different args & same return type
-    jjj: {
-      type: Function as PropType<
-        ((arg1: string) => string) | ((arg1: string, arg2: string) => string)
-      >,
-      required: true as true
-    },
-    kkk: null,
-    validated: {
-      type: String,
-      // validator requires explicit annotation
-      validator: (val: unknown) => val !== ''
-    },
-    date: Date,
-    l: [Date],
-    ll: [Date, Number],
-    lll: [String, Number]
-  }
-
-  const MyComponent = defineComponent({
-    props,
-    setup(props) {
-      // type assertion. See https://github.com/SamVerschueren/tsd
-      expectType<ExpectedProps['a']>(props.a)
-      expectType<ExpectedProps['b']>(props.b)
-      expectType<ExpectedProps['e']>(props.e)
-      expectType<ExpectedProps['h']>(props.h)
-      expectType<ExpectedProps['j']>(props.j)
-      expectType<ExpectedProps['bb']>(props.bb)
-      expectType<ExpectedProps['bbb']>(props.bbb)
-      expectType<ExpectedProps['bbbb']>(props.bbbb)
-      expectType<ExpectedProps['bbbbb']>(props.bbbbb)
-      expectType<ExpectedProps['cc']>(props.cc)
-      expectType<ExpectedProps['dd']>(props.dd)
-      expectType<ExpectedProps['ee']>(props.ee)
-      expectType<ExpectedProps['ff']>(props.ff)
-      expectType<ExpectedProps['ccc']>(props.ccc)
-      expectType<ExpectedProps['ddd']>(props.ddd)
-      expectType<ExpectedProps['eee']>(props.eee)
-      expectType<ExpectedProps['fff']>(props.fff)
-      expectType<ExpectedProps['hhh']>(props.hhh)
-      expectType<ExpectedProps['ggg']>(props.ggg)
-      expectType<ExpectedProps['ffff']>(props.ffff)
-      if (typeof props.iii !== 'function') {
-        expectType<undefined>(props.iii)
-      }
-      expectType<ExpectedProps['iii']>(props.iii)
-      expectType<IsUnion<typeof props.jjj>>(true)
-      expectType<ExpectedProps['jjj']>(props.jjj)
-      expectType<ExpectedProps['kkk']>(props.kkk)
-      expectType<ExpectedProps['validated']>(props.validated)
-      expectType<ExpectedProps['date']>(props.date)
-      expectType<ExpectedProps['l']>(props.l)
-      expectType<ExpectedProps['ll']>(props.ll)
-      expectType<ExpectedProps['lll']>(props.lll)
-
-      // @ts-expect-error props should be readonly
-      expectError((props.a = 1))
-
-      // setup context
-      return {
-        c: ref(1),
-        d: {
-          e: ref('hi')
-        },
-        f: reactive({
-          g: ref('hello' as GT)
-        })
-      }
-    },
-    provide() {
-      return {}
-    },
-    render() {
-      const props = this.$props
-      expectType<ExpectedProps['a']>(props.a)
-      expectType<ExpectedProps['b']>(props.b)
-      expectType<ExpectedProps['e']>(props.e)
-      expectType<ExpectedProps['h']>(props.h)
-      expectType<ExpectedProps['bb']>(props.bb)
-      expectType<ExpectedProps['cc']>(props.cc)
-      expectType<ExpectedProps['dd']>(props.dd)
-      expectType<ExpectedProps['ee']>(props.ee)
-      expectType<ExpectedProps['ff']>(props.ff)
-      expectType<ExpectedProps['ccc']>(props.ccc)
-      expectType<ExpectedProps['ddd']>(props.ddd)
-      expectType<ExpectedProps['eee']>(props.eee)
-      expectType<ExpectedProps['fff']>(props.fff)
-      expectType<ExpectedProps['hhh']>(props.hhh)
-      expectType<ExpectedProps['ggg']>(props.ggg)
-      if (typeof props.iii !== 'function') {
-        expectType<undefined>(props.iii)
-      }
-      expectType<ExpectedProps['iii']>(props.iii)
-      expectType<IsUnion<typeof props.jjj>>(true)
-      expectType<ExpectedProps['jjj']>(props.jjj)
-      expectType<ExpectedProps['kkk']>(props.kkk)
-
-      // @ts-expect-error props should be readonly
-      expectError((props.a = 1))
-
-      // should also expose declared props on `this`
-      expectType<ExpectedProps['a']>(this.a)
-      expectType<ExpectedProps['b']>(this.b)
-      expectType<ExpectedProps['e']>(this.e)
-      expectType<ExpectedProps['h']>(this.h)
-      expectType<ExpectedProps['bb']>(this.bb)
-      expectType<ExpectedProps['cc']>(this.cc)
-      expectType<ExpectedProps['dd']>(this.dd)
-      expectType<ExpectedProps['ee']>(this.ee)
-      expectType<ExpectedProps['ff']>(this.ff)
-      expectType<ExpectedProps['ccc']>(this.ccc)
-      expectType<ExpectedProps['ddd']>(this.ddd)
-      expectType<ExpectedProps['eee']>(this.eee)
-      expectType<ExpectedProps['fff']>(this.fff)
-      expectType<ExpectedProps['hhh']>(this.hhh)
-      expectType<ExpectedProps['ggg']>(this.ggg)
-      if (typeof this.iii !== 'function') {
-        expectType<undefined>(this.iii)
-      }
-      expectType<ExpectedProps['iii']>(this.iii)
-      const { jjj } = this
-      expectType<IsUnion<typeof jjj>>(true)
-      expectType<ExpectedProps['jjj']>(this.jjj)
-      expectType<ExpectedProps['kkk']>(this.kkk)
-
-      // @ts-expect-error props on `this` should be readonly
-      expectError((this.a = 1))
-
-      // assert setup context unwrapping
-      expectType<number>(this.c)
-      expectType<string>(this.d.e.value)
-      expectType<GT>(this.f.g)
-
-      // setup context properties should be mutable
-      this.c = 2
-
-      return null
-    }
-  })
-
-  expectType<Component>(MyComponent)
-
-  // Test TSX
-  expectType<JSX.Element>(
-    <MyComponent
-      a={1}
-      b="b"
-      bb="bb"
-      e={() => {}}
-      cc={['cc']}
-      dd={{ n: 1 }}
-      ee={() => 'ee'}
-      ccc={['ccc']}
-      ddd={['ddd']}
-      eee={() => ({ a: 'eee' })}
-      fff={(a, b) => ({ a: a > +b })}
-      hhh={false}
-      ggg="foo"
-      jjj={() => ''}
-      // should allow class/style as attrs
-      class="bar"
-      style={{ color: 'red' }}
-      // // should allow key
-      key={'foo'}
-      // // should allow ref
-      ref={'foo'}
-    />
-  )
-
-  // @ts-expect-error missing required props
-  expectError(<MyComponent />)
-  expectError(
-    // @ts-expect-error wrong prop types
-    <MyComponent a={'wrong type'} b="foo" dd={{ n: 1 }} ddd={['foo']} />
-  )
-  // @ts-expect-error wrong prop types
-  expectError(<MyComponent ggg="baz" />)
-  // @ts-expect-error
-  expectError(<MyComponent b="foo" dd={{ n: 'string' }} ddd={['foo']} />)
-})
-
-// describe('type inference w/ optional props declaration', () => {
-//   const MyComponent = defineComponent<{ a: string[]; msg: string }>({
-//     setup(props) {
-//       expectType<string>(props.msg)
-//       expectType<string[]>(props.a)
-//       return {
-//         b: 1
-//       }
-//     }
-//   })
-
-//   expectType<JSX.Element>(<MyComponent msg="1" a={['1']} />)
-//   // @ts-expect-error
-//   expectError(<MyComponent />)
-//   // @ts-expect-error
-//   expectError(<MyComponent msg="1" />)
-// })
-
-// describe('type inference w/ direct setup function', () => {
-//   const MyComponent = defineComponent((_props: { msg: string }) => {})
-//   expectType<JSX.Element>(<MyComponent msg="foo" />)
-//   // @ts-expect-error
-//   expectError(<MyComponent />)
-//   expectError(<MyComponent msg="1" />)
-// })
-
-describe('type inference w/ array props declaration', () => {
-  const MyComponent = defineComponent({
-    props: ['a', 'b'],
-    setup(props) {
-      // @ts-expect-error props should be readonly
-      expectError((props.a = 1))
-      expectType<any>(props.a)
-      expectType<any>(props.b)
-      return {
-        c: 1
-      }
-    },
-    render() {
-      expectType<any>(this.$props.a)
-      expectType<any>(this.$props.b)
-      // @ts-expect-error
-      expectError((this.$props.a = 1))
-      expectType<any>(this.a)
-      expectType<any>(this.b)
-      expectType<number>(this.c)
-    }
-  })
-  expectType<JSX.Element>(<MyComponent a={[1, 2]} b="b" />)
-  // @ts-expect-error
-  expectError(<MyComponent other="other" />)
-})
-
-describe('type inference w/ options API', () => {
-  defineComponent({
-    props: { a: Number },
-    setup() {
-      return {
-        b: 123
-      }
-    },
-    data() {
-      // Limitation: we cannot expose the return result of setup() on `this`
-      // here in data() - somehow that would mess up the inference
-      expectType<number | undefined>(this.a)
-      return {
-        c: this.a || 123,
-        someRef: ref(0)
-      }
-    },
-    computed: {
-      d() {
-        expectType<number>(this.b)
-        return this.b + 1
-      },
-      e: {
-        get() {
-          expectType<number>(this.b)
-          expectType<number>(this.d)
-
-          return this.b + this.d
-        },
-        set(v: number) {
-          expectType<number>(this.b)
-          expectType<number>(this.d)
-          expectType<number>(v)
-        }
-      }
-    },
-    watch: {
-      a() {
-        expectType<number>(this.b)
-        this.b + 1
-      }
-    },
-    created() {
-      // props
-      expectType<number | undefined>(this.a)
-      // returned from setup()
-      expectType<number>(this.b)
-      // returned from data()
-      expectType<number>(this.c)
-      // computed
-      expectType<number>(this.d)
-      // computed get/set
-      expectType<number>(this.e)
-      // expectType<number>(this.someRef)
-    },
-    methods: {
-      doSomething() {
-        // props
-        expectType<number | undefined>(this.a)
-        // returned from setup()
-        expectType<number>(this.b)
-        // returned from data()
-        expectType<number>(this.c)
-        // computed
-        expectType<number>(this.d)
-        // computed get/set
-        expectType<number>(this.e)
-      },
-      returnSomething() {
-        return this.a
-      }
-    },
-    render() {
-      // props
-      expectType<number | undefined>(this.a)
-      // returned from setup()
-      expectType<number>(this.b)
-      // returned from data()
-      expectType<number>(this.c)
-      // computed
-      expectType<number>(this.d)
-      // computed get/set
-      expectType<number>(this.e)
-      // method
-      expectType<() => number | undefined>(this.returnSomething)
-    }
-  })
-})
-
-describe('with mixins', () => {
-  const MixinA = defineComponent({
-    emits: ['bar'],
-    props: {
-      aP1: {
-        type: String,
-        default: 'aP1'
-      },
-      aP2: Boolean
-    },
-    data() {
-      return {
-        a: 1
-      }
-    }
-  })
-  const MixinB = defineComponent({
-    props: ['bP1', 'bP2'],
-    data() {
-      return {
-        b: 2
-      }
-    }
-  })
-  const MixinC = defineComponent({
-    data() {
-      return {
-        c: 3
-      }
-    }
-  })
-  const MixinD = defineComponent({
-    mixins: [MixinA],
-    data() {
-      //@ts-expect-error computed are not available on data()
-      expectError<number>(this.dC1)
-      //@ts-expect-error computed are not available on data()
-      expectError<string>(this.dC2)
-
-      return {
-        d: 4
-      }
-    },
-    setup(props) {
-      expectType<string>(props.aP1)
-    },
-    computed: {
-      dC1() {
-        return this.d + this.a
-      },
-      dC2() {
-        return this.aP1 + 'dC2'
-      }
-    }
-  })
-  const MyComponent = defineComponent({
-    mixins: [MixinA, MixinB, MixinC, MixinD],
-    emits: ['click'],
-    props: {
-      // required should make property non-void
-      z: {
-        type: String,
-        required: true
-      }
-    },
-
-    data(vm) {
-      expectType<number>(vm.a)
-      expectType<number>(vm.b)
-      expectType<number>(vm.c)
-      expectType<number>(vm.d)
-
-      // should also expose declared props on `this`
-      expectType<number>(this.a)
-      expectType<string>(this.aP1)
-      expectType<boolean | undefined>(this.aP2)
-      expectType<number>(this.b)
-      expectType<any>(this.bP1)
-      expectType<number>(this.c)
-      expectType<number>(this.d)
-
-      return {}
-    },
-
-    setup(props) {
-      expectType<string>(props.z)
-      // props
-      // expectType<((...args: any[]) => any) | undefined>(props.onClick)
-      // from Base
-      // expectType<((...args: any[]) => any) | undefined>(props.onBar)
-      expectType<string>(props.aP1)
-      expectType<boolean | undefined>(props.aP2)
-      expectType<any>(props.bP1)
-      expectType<any>(props.bP2)
-      expectType<string>(props.z)
-    },
-    render() {
-      const props = this.$props
-      // props
-      // expectType<((...args: any[]) => any) | undefined>(props.onClick)
-      // from Base
-      // expectType<((...args: any[]) => any) | undefined>(props.onBar)
-      expectType<string>(props.aP1)
-      expectType<boolean | undefined>(props.aP2)
-      expectType<any>(props.bP1)
-      expectType<any>(props.bP2)
-      expectType<string>(props.z)
-
-      const data = this.$data
-      expectType<number>(data.a)
-      expectType<number>(data.b)
-      expectType<number>(data.c)
-      expectType<number>(data.d)
-
-      // should also expose declared props on `this`
-      expectType<number>(this.a)
-      expectType<string>(this.aP1)
-      expectType<boolean | undefined>(this.aP2)
-      expectType<number>(this.b)
-      expectType<any>(this.bP1)
-      expectType<number>(this.c)
-      expectType<number>(this.d)
-      expectType<number>(this.dC1)
-      expectType<string>(this.dC2)
-
-      // props should be readonly
-      // @ts-expect-error
-      expectError((this.aP1 = 'new'))
-      // @ts-expect-error
-      expectError((this.z = 1))
-
-      // props on `this` should be readonly
-      // @ts-expect-error
-      expectError((this.bP1 = 1))
-
-      // string value can not assigned to number type value
-      // @ts-expect-error
-      expectError((this.c = '1'))
-
-      // setup context properties should be mutable
-      this.d = 5
-
-      return null
-    }
-  })
-
-  // Test TSX
-  expectType<JSX.Element>(
-    <MyComponent aP1={'aP'} aP2 bP1={1} bP2={[1, 2]} z={'z'} />
-  )
-
-  // missing required props
-  // @ts-expect-error
-  expectError(<MyComponent />)
-
-  // wrong prop types
-  // @ts-expect-error
-  expectError(<MyComponent aP1="ap" aP2={'wrong type'} bP1="b" z={'z'} />)
-  // @ts-expect-error
-  expectError(<MyComponent aP1={1} bP2={[1]} />)
-})
-
-describe('with extends', () => {
-  const Base = defineComponent({
-    props: {
-      aP1: Boolean,
-      aP2: {
-        type: Number,
-        default: 2
-      }
-    },
-    data() {
-      return {
-        a: 1
-      }
-    },
-    computed: {
-      c(): number {
-        return this.aP2 + this.a
-      }
-    }
-  })
-  const MyComponent = defineComponent({
-    extends: Base,
-    props: {
-      // required should make property non-void
-      z: {
-        type: String,
-        required: true
-      }
-    },
-    render() {
-      const props = this.$props
-      // props
-      expectType<boolean | undefined>(props.aP1)
-      expectType<number>(props.aP2)
-      expectType<string>(props.z)
-
-      const data = this.$data
-      expectType<number>(data.a)
-
-      // should also expose declared props on `this`
-      expectType<number>(this.a)
-      expectType<boolean | undefined>(this.aP1)
-      expectType<number>(this.aP2)
-
-      // setup context properties should be mutable
-      this.a = 5
-
-      return null
-    }
-  })
-
-  // Test TSX
-  expectType<JSX.Element>(<MyComponent aP2={3} aP1 z={'z'} />)
-
-  // missing required props
-  // @ts-expect-error
-  expectError(<MyComponent />)
-
-  // wrong prop types
-  // @ts-expect-error
-  expectError(<MyComponent aP2={'wrong type'} z={'z'} />)
-  // @ts-expect-error
-  expectError(<MyComponent aP1={3} />)
-})
-
-describe('extends with mixins', () => {
-  const Mixin = defineComponent({
-    emits: ['bar'],
-    props: {
-      mP1: {
-        type: String,
-        default: 'mP1'
-      },
-      mP2: Boolean,
-      mP3: {
-        type: Boolean,
-        required: true
-      }
-    },
-    data() {
-      return {
-        a: 1
-      }
-    }
-  })
-  const Base = defineComponent({
-    emits: ['foo'],
-    props: {
-      p1: Boolean,
-      p2: {
-        type: Number,
-        default: 2
-      },
-      p3: {
-        type: Boolean,
-        required: true
-      }
-    },
-    data() {
-      return {
-        b: 2
-      }
-    },
-    computed: {
-      c(): number {
-        return this.p2 + this.b
-      }
-    }
-  })
-  const MyComponent = defineComponent({
-    extends: Base,
-    mixins: [Mixin],
-    emits: ['click'],
-    props: {
-      // required should make property non-void
-      z: {
-        type: String,
-        required: true
-      }
-    },
-    render() {
-      const props = this.$props
-      // props
-      // expectType<((...args: any[]) => any) | undefined>(props.onClick)
-      // from Mixin
-      // expectType<((...args: any[]) => any) | undefined>(props.onBar)
-      // from Base
-      // expectType<((...args: any[]) => any) | undefined>(props.onFoo)
-      expectType<boolean | undefined>(props.p1)
-      expectType<number>(props.p2)
-      expectType<string>(props.z)
-      expectType<string>(props.mP1)
-      expectType<boolean | undefined>(props.mP2)
-
-      const data = this.$data
-      expectType<number>(data.a)
-      expectType<number>(data.b)
-
-      // should also expose declared props on `this`
-      expectType<number>(this.a)
-      expectType<number>(this.b)
-      expectType<boolean | undefined>(this.p1)
-      expectType<number>(this.p2)
-      expectType<string>(this.mP1)
-      expectType<boolean | undefined>(this.mP2)
-
-      // setup context properties should be mutable
-      this.a = 5
-
-      return null
-    }
-  })
-
-  // Test TSX
-  expectType<JSX.Element>(<MyComponent mP1="p1" mP2 mP3 p1 p2={1} p3 z={'z'} />)
-
-  // mP1, mP2, p1, and p2 have default value. these are not required
-  expectType<JSX.Element>(<MyComponent mP3 p3 z={'z'} />)
-
-  // missing required props
-  // @ts-expect-error
-  expectError(<MyComponent mP3 p3 /* z='z' */ />)
-  // missing required props from mixin
-  // @ts-expect-error
-  expectError(<MyComponent /* mP3 */ p3 z="z" />)
-  // missing required props from extends
-  // @ts-expect-error
-  expectError(<MyComponent mP3 /* p3 */ z="z" />)
-
-  // wrong prop types
-  // @ts-expect-error
-  expectError(<MyComponent p2={'wrong type'} z={'z'} />)
-  // @ts-expect-error
-  expectError(<MyComponent mP1={3} />)
-
-  // #3468
-  const CompWithD = defineComponent({
-    data() {
-      return { foo: 1 }
-    }
-  })
-  const CompWithC = defineComponent({
-    computed: {
-      foo() {
-        return 1
-      }
-    }
-  })
-  const CompWithM = defineComponent({ methods: { foo() {} } })
-  const CompEmpty = defineComponent({})
-
-  defineComponent({
-    mixins: [CompWithD, CompEmpty],
-    mounted() {
-      expectType<number>(this.foo)
-    }
-  })
-  defineComponent({
-    mixins: [CompWithC, CompEmpty],
-    mounted() {
-      expectType<number>(this.foo)
-    }
-  })
-  defineComponent({
-    mixins: [CompWithM, CompEmpty],
-    mounted() {
-      expectType<() => void>(this.foo)
-    }
-  })
-})
-
-describe('defineComponent', () => {
-  test('should accept components defined with defineComponent', () => {
-    const comp = defineComponent({})
-    defineComponent({
-      components: { comp }
-    })
-  })
-})
-
-describe('emits', () => {
-  // Note: for TSX inference, ideally we want to map emits to onXXX props,
-  // but that requires type-level string constant concatenation as suggested in
-  // https://github.com/Microsoft/TypeScript/issues/12754
-
-  // The workaround for TSX users is instead of using emits, declare onXXX props
-  // and call them instead. Since `v-on:click` compiles to an `onClick` prop,
-  // this would also support other users consuming the component in templates
-  // with `v-on` listeners.
-
-  // with object emits
-  defineComponent({
-    emits: {
-      click: (n: number) => typeof n === 'number',
-      input: (b: string) => b.length > 1
-    },
-    setup(props, { emit }) {
-      // expectType<((n: number) => boolean) | undefined>(props.onClick)
-      // expectType<((b: string) => boolean) | undefined>(props.onInput)
-      emit('click', 1)
-      emit('input', 'foo')
-      //  @ts-expect-error
-      expectError(emit('nope'))
-      //  @ts-expect-error
-      expectError(emit('click'))
-      //  @ts-expect-error
-      expectError(emit('click', 'foo'))
-      //  @ts-expect-error
-      expectError(emit('input'))
-      //  @ts-expect-error
-      expectError(emit('input', 1))
-    },
-    created() {
-      this.$emit('click', 1)
-      this.$emit('input', 'foo')
-      //  @ts-expect-error
-      expectError(this.$emit('nope'))
-      //  @ts-expect-error
-      expectError(this.$emit('click'))
-      //  @ts-expect-error
-      expectError(this.$emit('click', 'foo'))
-      //  @ts-expect-error
-      expectError(this.$emit('input'))
-      //  @ts-expect-error
-      expectError(this.$emit('input', 1))
-    },
-    mounted() {
-      // #3599
-      this.$nextTick(function () {
-        // this should be bound to this instance
-        this.$emit('click', 1)
-        this.$emit('input', 'foo')
-        //  @ts-expect-error
-        expectError(this.$emit('nope'))
-        //  @ts-expect-error
-        expectError(this.$emit('click'))
-        //  @ts-expect-error
-        expectError(this.$emit('click', 'foo'))
-        //  @ts-expect-error
-        expectError(this.$emit('input'))
-        //  @ts-expect-error
-        expectError(this.$emit('input', 1))
-      })
-    }
-  })
-
-  // with array emits
-  defineComponent({
-    emits: ['foo', 'bar'],
-    setup(props, { emit }) {
-      // expectType<((...args: any[]) => any) | undefined>(props.onFoo)
-      // expectType<((...args: any[]) => any) | undefined>(props.onBar)
-      emit('foo')
-      emit('foo', 123)
-      emit('bar')
-      //  @ts-expect-error
-      expectError(emit('nope'))
-    },
-    created() {
-      this.$emit('foo')
-      this.$emit('foo', 123)
-      this.$emit('bar')
-      //  @ts-expect-error
-      expectError(this.$emit('nope'))
-    }
-  })
-
-  // with tsx
-  const Component = defineComponent({
-    emits: {
-      click: (n: number) => typeof n === 'number'
-    },
-    setup(props, { emit }) {
-      // expectType<((n: number) => any) | undefined>(props.onClick)
-      emit('click', 1)
-      //  @ts-expect-error
-      expectError(emit('click'))
-      //  @ts-expect-error
-      expectError(emit('click', 'foo'))
-    }
-  })
-
-  // defineComponent({
-  //   render() {
-  //     return (
-  //       <Component
-  //         onClick={(n: number) => {
-  //           return n + 1
-  //         }}
-  //       />
-  //     )
-  //   }
-  // })
-
-  // without emits
-  defineComponent({
-    setup(props, { emit }) {
-      emit('test', 1)
-      emit('test')
-    }
-  })
-
-  // emit should be valid when ComponentPublicInstance is used.
-  const instance = {} as ComponentPublicInstance
-  instance.$emit('test', 1)
-  instance.$emit('test')
-
-  // `this` should be void inside of emits validators
-  defineComponent({
-    props: ['bar'],
-    emits: {
-      foo(): boolean {
-        // @ts-expect-error
-        return this.bar === 3
-      }
-    }
-  })
-})
-
-// describe('componentOptions setup should be `SetupContext`', () => {
-//   expectType<ComponentOptions['setup']>(
-//     {} as (props: Record<string, any>, ctx: SetupContext) => any
-//   )
-// })
-
-describe('extract instance type', () => {
-  const Base = defineComponent({
-    props: {
-      baseA: {
-        type: Number,
-        default: 1
-      }
-    }
-  })
-  const MixinA = defineComponent({
-    props: {
-      mA: {
-        type: String,
-        default: ''
-      }
-    }
-  })
-  const CompA = defineComponent({
-    extends: Base,
-    mixins: [MixinA],
-    props: {
-      a: {
-        type: Boolean,
-        default: false
-      },
-      b: {
-        type: String,
-        required: true
-      },
-      c: Number
-    }
-  })
-
-  const compA = {} as InstanceType<typeof CompA>
-
-  expectType<boolean>(compA.a)
-  expectType<string>(compA.b)
-  expectType<number | undefined>(compA.c)
-  // mixins
-  expectType<string>(compA.mA)
-  // extends
-  expectType<number>(compA.baseA)
-
-  //  @ts-expect-error
-  expectError((compA.a = true))
-  //  @ts-expect-error
-  expectError((compA.b = 'foo'))
-  //  @ts-expect-error
-  expectError((compA.c = 1))
-  //  @ts-expect-error
-  expectError((compA.mA = 'foo'))
-  //  @ts-expect-error
-  expectError((compA.baseA = 1))
-})
-
-// #5948
-describe('DefineComponent should infer correct types when assigning to Component', () => {
-  let component: Component
-  component = defineComponent({
-    setup(_, { attrs, slots }) {
-      // @ts-expect-error should not be any
-      expectType<[]>(attrs)
-      // @ts-expect-error should not be any
-      expectType<[]>(slots)
-    }
-  })
-  expectType<Component>(component)
-})
-
-// #5969
-describe('should allow to assign props', () => {
-  const Child = defineComponent({
-    props: {
-      bar: String
-    }
-  })
-
-  const Parent = defineComponent({
-    props: {
-      ...Child.props,
-      foo: String
-    }
-  })
-
-  const child = new Child()
-  expectType<JSX.Element>(<Parent {...child.$props} />)
-})
-
-// check if defineComponent can be exported
-export default {
-  // no props
-  b: defineComponent({
-    data() {
-      return {}
-    }
-  }),
-  c: defineComponent({
-    props: ['a']
-  }),
-  d: defineComponent({
-    props: {
-      a: Number
-    }
-  })
-}
-
-describe('functional w/ array props', () => {
-  const Foo = defineComponent({
-    functional: true,
-    props: ['foo'],
-    render(h, ctx) {
-      ctx.props.foo
-      // @ts-expect-error
-      ctx.props.bar
-    }
-  })
-
-  ;<Foo foo="hi" />
-  // @ts-expect-error
-  ;<Foo bar={123} />
-})
-
-describe('functional w/ object props', () => {
-  const Foo = defineComponent({
-    functional: true,
-    props: {
-      foo: String
-    },
-    render(h, ctx) {
-      ctx.props.foo
-      // @ts-expect-error
-      ctx.props.bar
-    }
-  })
-
-  ;<Foo foo="hi" />
-  // @ts-expect-error
-  ;<Foo foo={123} />
-  // @ts-expect-error
-  ;<Foo bar={123} />
-})
-
-// #12628
-defineComponent({
-  components: {
-    App: defineComponent({})
-  },
-  data() {
-    return {}
-  },
-  provide(): any {
-    return {
-      fetchData: this.fetchData
-    }
-  },
-  created() {
-    this.fetchData()
-  },
-  methods: {
-    fetchData() {
-      throw new Error('Not implemented.')
-    }
-  }
-})
-
-const X = defineComponent({
-  methods: {
-    foo() {
-      return 123
-    }
-  }
-})
-
-// Missing / mismatching Vue 2 properties
-// https://github.com/vuejs/vue/issues/12628#issuecomment-1177258223
-defineComponent({
-  render(h) {
-    // vue 2
-    this.$listeners
-    this.$on('foo', () => {})
-    this.$ssrContext
-    this.$isServer
-    this.$children[0].$root.$children
-
-    // type casting refs
-    const foo = this.$refs.foo as InstanceType<typeof X>
-    foo.foo().toExponential()
-
-    return h('div', {}, [...this.$slots.default!])
-  }
-})
-
-describe('constructor attach custom properties', () => {
-  // #12742 allow attaching custom properties (consistent with v3)
-  const Foo = defineComponent({})
-  Foo.foobar = 123
-})
-
-describe('constructor instance type', () => {
-  const Comp = defineComponent({
-    data() {
-      return {
-        a: 1
-      }
-    },
-
-    computed: {
-      ac() {
-        return 1
-      }
-    },
-
-    methods: {
-      callA(b: number) {
-        return b
-      }
-    },
-
-    setup() {
-      return {
-        sa: '1'
-      }
-    }
-  })
-
-  const comp = new Comp()
-
-  expectType<number>(comp.a)
-  expectType<number>(comp.ac)
-  expectType<string>(comp.sa)
-  expectType<(b: number) => number>(comp.callA)
-})
-
-describe('should report non-existent properties in instance', () => {
-  const Foo = defineComponent({})
-  const instance = new Foo()
-  // @ts-expect-error
-  instance.foo
-
-  const Foo2 = defineComponent({
-    data() {
-      return {}
-    },
-    methods: {
-      example() {}
-    }
-  })
-  const instance2 = new Foo2()
-  // @ts-expect-error
-  instance2.foo
-})
diff --git a/types/test/v3/inject-test.ts b/types/test/v3/inject-test.ts
deleted file mode 100644
index dd294be724d..00000000000
--- a/types/test/v3/inject-test.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { InjectionKey, provide, inject } from '../../index'
-import { expectType } from '../utils'
-
-const key: InjectionKey<number> = Symbol()
-
-provide(key, 1)
-// @ts-expect-error
-provide(key, 'foo')
-
-expectType<number | undefined>(inject(key))
-expectType<number>(inject(key, 1))
-expectType<number>(inject(key, () => 1, true /* treatDefaultAsFactory */))
-
-expectType<() => number>(inject('foo', () => 1))
-expectType<() => number>(inject('foo', () => 1, false))
-expectType<number>(inject('foo', () => 1, true))
diff --git a/types/test/v3/reactivity-test.ts b/types/test/v3/reactivity-test.ts
deleted file mode 100644
index c357bf8d5c7..00000000000
--- a/types/test/v3/reactivity-test.ts
+++ /dev/null
@@ -1,398 +0,0 @@
-import {
-  Ref,
-  ref,
-  shallowRef,
-  isRef,
-  unref,
-  reactive,
-  toRef,
-  toRefs,
-  ToRefs,
-  shallowReactive,
-  readonly,
-  markRaw,
-  shallowReadonly,
-  set,
-  del
-} from '../../index'
-import { IsUnion, describe, expectType } from '../utils'
-
-function plainType(arg: number | Ref<number>) {
-  // ref coercing
-  const coerced = ref(arg)
-  expectType<Ref<number>>(coerced)
-
-  // isRef as type guard
-  if (isRef(arg)) {
-    expectType<Ref<number>>(arg)
-  }
-
-  // ref unwrapping
-  expectType<number>(unref(arg))
-
-  // ref inner type should be unwrapped
-  const nestedRef = ref({
-    foo: ref(1)
-  })
-  expectType<{ foo: number }>(nestedRef.value)
-
-  // ref boolean
-  const falseRef = ref(false)
-  expectType<Ref<boolean>>(falseRef)
-  expectType<boolean>(falseRef.value)
-
-  // ref true
-  const trueRef = ref<true>(true)
-  expectType<Ref<true>>(trueRef)
-  expectType<true>(trueRef.value)
-
-  // tuple
-  expectType<[number, string]>(unref(ref([1, '1'])))
-
-  interface IteratorFoo {
-    [Symbol.iterator]: any
-  }
-
-  // with symbol
-  expectType<Ref<IteratorFoo | null | undefined>>(
-    ref<IteratorFoo | null | undefined>()
-  )
-
-  // should not unwrap ref inside arrays
-  const arr = ref([1, new Map<string, any>(), ref('1')]).value
-  const value = arr[0]
-  if (isRef(value)) {
-    expectType<Ref>(value)
-  } else if (typeof value === 'number') {
-    expectType<number>(value)
-  } else {
-    // should narrow down to Map type
-    // and not contain any Ref type
-    expectType<Map<string, any>>(value)
-  }
-
-  // should still unwrap in objects nested in arrays
-  const arr2 = ref([{ a: ref(1) }]).value
-  expectType<number>(arr2[0].a)
-}
-
-plainType(1)
-
-function bailType(arg: HTMLElement | Ref<HTMLElement>) {
-  // ref coercing
-  const coerced = ref(arg)
-  expectType<Ref<HTMLElement>>(coerced)
-
-  // isRef as type guard
-  if (isRef(arg)) {
-    expectType<Ref<HTMLElement>>(arg)
-  }
-
-  // ref unwrapping
-  expectType<HTMLElement>(unref(arg))
-
-  // ref inner type should be unwrapped
-  // eslint-disable-next-line no-restricted-globals
-  const nestedRef = ref({ foo: ref(document.createElement('DIV')) })
-
-  expectType<Ref<{ foo: HTMLElement }>>(nestedRef)
-  expectType<{ foo: HTMLElement }>(nestedRef.value)
-}
-// eslint-disable-next-line no-restricted-globals
-const el = document.createElement('DIV')
-bailType(el)
-
-function withSymbol() {
-  const customSymbol = Symbol()
-  const obj = {
-    [Symbol.asyncIterator]: ref(1),
-    [Symbol.hasInstance]: { a: ref('a') },
-    [Symbol.isConcatSpreadable]: { b: ref(true) },
-    [Symbol.iterator]: [ref(1)],
-    [Symbol.match]: new Set<Ref<number>>(),
-    [Symbol.matchAll]: new Map<number, Ref<string>>(),
-    [Symbol.replace]: { arr: [ref('a')] },
-    [Symbol.search]: { set: new Set<Ref<number>>() },
-    [Symbol.species]: { map: new Map<number, Ref<string>>() },
-    [Symbol.split]: new WeakSet<Ref<boolean>>(),
-    [Symbol.toPrimitive]: new WeakMap<Ref<boolean>, string>(),
-    [Symbol.toStringTag]: { weakSet: new WeakSet<Ref<boolean>>() },
-    [Symbol.unscopables]: { weakMap: new WeakMap<Ref<boolean>, string>() },
-    [customSymbol]: { arr: [ref(1)] }
-  }
-
-  const objRef = ref(obj)
-
-  expectType<Ref<number>>(objRef.value[Symbol.asyncIterator])
-  expectType<{ a: Ref<string> }>(objRef.value[Symbol.hasInstance])
-  expectType<{ b: Ref<boolean> }>(objRef.value[Symbol.isConcatSpreadable])
-  expectType<Ref<number>[]>(objRef.value[Symbol.iterator])
-  expectType<Set<Ref<number>>>(objRef.value[Symbol.match])
-  expectType<Map<number, Ref<string>>>(objRef.value[Symbol.matchAll])
-  expectType<{ arr: Ref<string>[] }>(objRef.value[Symbol.replace])
-  expectType<{ set: Set<Ref<number>> }>(objRef.value[Symbol.search])
-  expectType<{ map: Map<number, Ref<string>> }>(objRef.value[Symbol.species])
-  expectType<WeakSet<Ref<boolean>>>(objRef.value[Symbol.split])
-  expectType<WeakMap<Ref<boolean>, string>>(objRef.value[Symbol.toPrimitive])
-  expectType<{ weakSet: WeakSet<Ref<boolean>> }>(
-    objRef.value[Symbol.toStringTag]
-  )
-  expectType<{ weakMap: WeakMap<Ref<boolean>, string> }>(
-    objRef.value[Symbol.unscopables]
-  )
-  expectType<{ arr: Ref<number>[] }>(objRef.value[customSymbol])
-}
-
-withSymbol()
-
-const state = reactive({
-  foo: {
-    value: 1,
-    label: 'bar'
-  }
-})
-
-expectType<string>(state.foo.label)
-
-// shallowRef
-type Status = 'initial' | 'ready' | 'invalidating'
-const shallowStatus = shallowRef<Status>('initial')
-if (shallowStatus.value === 'initial') {
-  expectType<Ref<Status>>(shallowStatus)
-  expectType<Status>(shallowStatus.value)
-  shallowStatus.value = 'invalidating'
-}
-
-const refStatus = ref<Status>('initial')
-if (refStatus.value === 'initial') {
-  expectType<Ref<Status>>(shallowStatus)
-  expectType<Status>(shallowStatus.value)
-  refStatus.value = 'invalidating'
-}
-
-// proxyRefs: should return `reactive` directly
-// const r1 = reactive({
-//   k: 'v'
-// })
-// const p1 = proxyRefs(r1)
-// expectType<typeof r1>(p1)
-
-// // proxyRefs: `ShallowUnwrapRef`
-// const r2 = {
-//   a: ref(1),
-//   obj: {
-//     k: ref('foo')
-//   }
-// }
-// const p2 = proxyRefs(r2)
-// expectType<number>(p2.a)
-// expectType<Ref<string>>(p2.obj.k)
-
-// toRef and toRefs
-{
-  const obj: {
-    a: number
-    b: Ref<number>
-    c: number | string
-  } = {
-    a: 1,
-    b: ref(1),
-    c: 1
-  }
-
-  // toRef
-  expectType<Ref<number>>(toRef(obj, 'a'))
-  expectType<Ref<number>>(toRef(obj, 'b'))
-  // Should not distribute Refs over union
-  expectType<Ref<number | string>>(toRef(obj, 'c'))
-
-  // toRefs
-  expectType<{
-    a: Ref<number>
-    b: Ref<number>
-    // Should not distribute Refs over union
-    c: Ref<number | string>
-  }>(toRefs(obj))
-
-  // Both should not do any unwrapping
-  const someReactive = shallowReactive({
-    a: {
-      b: ref(42)
-    }
-  })
-
-  const toRefResult = toRef(someReactive, 'a')
-  const toRefsResult = toRefs(someReactive)
-
-  expectType<Ref<number>>(toRefResult.value.b)
-  expectType<Ref<number>>(toRefsResult.a.value.b)
-
-  // #5188
-  const props = { foo: 1 } as { foo: any }
-  const { foo } = toRefs(props)
-  expectType<Ref<any>>(foo)
-}
-
-// toRef default value
-{
-  const obj: { x?: number } = {}
-  const x = toRef(obj, 'x', 1)
-  expectType<Ref<number>>(x)
-}
-
-// readonly() + ref()
-expectType<Readonly<Ref<number>>>(readonly(ref(1)))
-
-// #2687
-interface AppData {
-  state: 'state1' | 'state2' | 'state3'
-}
-
-const data: ToRefs<AppData> = toRefs(
-  reactive({
-    state: 'state1'
-  })
-)
-
-switch (data.state.value) {
-  case 'state1':
-    data.state.value = 'state2'
-    break
-  case 'state2':
-    data.state.value = 'state3'
-    break
-  case 'state3':
-    data.state.value = 'state1'
-    break
-}
-
-// #3954
-function testUnrefGenerics<T>(p: T | Ref<T>) {
-  expectType<T>(unref(p))
-}
-
-testUnrefGenerics(1)
-
-// #4771
-describe('shallow reactive in reactive', () => {
-  const baz = reactive({
-    foo: shallowReactive({
-      a: {
-        b: ref(42)
-      }
-    })
-  })
-
-  const foo = toRef(baz, 'foo')
-
-  expectType<Ref<number>>(foo.value.a.b)
-  expectType<number>(foo.value.a.b.value)
-})
-
-describe('shallow ref in reactive', () => {
-  const x = reactive({
-    foo: shallowRef({
-      bar: {
-        baz: ref(123),
-        qux: reactive({
-          z: ref(123)
-        })
-      }
-    })
-  })
-
-  expectType<Ref<number>>(x.foo.bar.baz)
-  expectType<number>(x.foo.bar.qux.z)
-})
-
-describe('ref in shallow ref', () => {
-  const x = shallowRef({
-    a: ref(123)
-  })
-
-  expectType<Ref<number>>(x.value.a)
-})
-
-describe('reactive in shallow ref', () => {
-  const x = shallowRef({
-    a: reactive({
-      b: ref(0)
-    })
-  })
-
-  expectType<number>(x.value.a.b)
-})
-
-describe('should support DeepReadonly', () => {
-  const r = readonly({ obj: { k: 'v' } })
-  // @ts-expect-error
-  expectError((r.obj = {}))
-  // @ts-expect-error
-  expectError((r.obj.k = 'x'))
-})
-
-// #4180
-describe('readonly ref', () => {
-  const r = readonly(ref({ count: 1 }))
-  expectType<Ref>(r)
-})
-
-describe('should support markRaw', () => {
-  class Test<T> {
-    item = {} as Ref<T>
-  }
-  const test = new Test<number>()
-  const plain = {
-    ref: ref(1)
-  }
-
-  const r = reactive({
-    class: {
-      raw: markRaw(test),
-      reactive: test
-    },
-    plain: {
-      raw: markRaw(plain),
-      reactive: plain
-    }
-  })
-
-  expectType<Test<number>>(r.class.raw)
-  // @ts-expect-error it should unwrap
-  expectType<Test<number>>(r.class.reactive)
-
-  expectType<Ref<number>>(r.plain.raw.ref)
-  // @ts-expect-error it should unwrap
-  expectType<Ref<number>>(r.plain.reactive.ref)
-})
-
-describe('shallowReadonly ref unwrap', () => {
-  const r = shallowReadonly({ count: { n: ref(1) } })
-  // @ts-expect-error
-  r.count = 2
-  expectType<Ref>(r.count.n)
-  r.count.n.value = 123
-})
-
-describe('set/del', () => {
-  set({}, 1, 'hi')
-  set([], 1, 'bye')
-  del({}, 'foo')
-  del([], 1)
-
-  // @ts-expect-error
-  set({}, 1)
-  // @ts-expect-error
-  del([], 'fse', 123)
-})
-
-
-{
-  //#12978
-  type Steps = { step: '1' } | { step: '2' }
-  const shallowUnionGenParam = shallowRef<Steps>({ step: '1' })
-  const shallowUnionAsCast = shallowRef({ step: '1' } as Steps)
-
-  expectType<IsUnion<typeof shallowUnionGenParam>>(false)
-  expectType<IsUnion<typeof shallowUnionAsCast>>(false)
-}
\ No newline at end of file
diff --git a/types/test/v3/setup-test.ts b/types/test/v3/setup-test.ts
deleted file mode 100644
index 328941fd0b4..00000000000
--- a/types/test/v3/setup-test.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-import Vue, { defineComponent, PropType } from '../../index'
-
-// object props
-Vue.extend({
-  props: {
-    foo: String,
-    bar: Number
-  },
-  setup(props) {
-    props.foo + 'foo'
-    props.bar + 123
-  }
-})
-
-// array props
-Vue.extend({
-  props: ['foo', 'bar'],
-  setup(props) {
-    props.foo
-    props.bar
-  }
-})
-
-// context
-Vue.extend({
-  setup(_props, ctx) {
-    if (ctx.attrs.id) {
-    }
-    ctx.emit('foo')
-    ctx.slots.default && ctx.slots.default()
-    ctx.expose({
-      a: 123
-    })
-  }
-})
-
-// object props
-defineComponent({
-  props: {
-    foo: String,
-    bar: Number
-  },
-  setup(props) {
-    // @ts-expect-error
-    props.foo.slice(1, 2)
-
-    props.foo?.slice(1, 2)
-
-    // @ts-expect-error
-    props.bar + 123
-
-    props.bar?.toFixed(2)
-  }
-})
-
-// array props
-defineComponent({
-  props: ['foo', 'bar'],
-  setup(props) {
-    props.foo
-    props.bar
-  }
-})
-
-// context
-defineComponent({
-  emits: ['foo'],
-  setup(_props, ctx) {
-    if (ctx.attrs.id) {
-    }
-    ctx.emit('foo')
-    // @ts-expect-error
-    ctx.emit('ok')
-    ctx.slots.default && ctx.slots.default()
-  },
-  methods: {
-    foo() {
-      this.$emit('foo')
-      // @ts-expect-error
-      this.$emit('bar')
-    }
-  }
-})
-
-defineComponent({
-  props: {
-    foo: null as any as PropType<{ a: number }>
-  },
-  data() {
-    this.foo?.a
-  },
-  setup(props) {
-    const res = props.foo?.a.toFixed(2)
-    // @ts-expect-error
-    res.charAt(1)
-    res?.charAt(1)
-  }
-})
-
-// #12568
-const vm = new Vue({
-  setup() {},
-  render: h => h({})
-})
-
-vm.$mount('#app')
diff --git a/types/test/v3/tsx-test.tsx b/types/test/v3/tsx-test.tsx
deleted file mode 100644
index 6a9dcce4fc1..00000000000
--- a/types/test/v3/tsx-test.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import { VNode, defineComponent, ref, RenderContext } from '../../index'
-import { expectType } from '../utils'
-
-expectType<VNode>(<div />)
-expectType<JSX.Element>(<div />)
-expectType<JSX.Element>(<div id="foo" />)
-expectType<JSX.Element>(<input value="foo" />)
-
-// @ts-expect-error style css property validation
-expectError(<div style={{ unknown: 123 }} />)
-
-// allow array styles and nested array styles
-expectType<JSX.Element>(<div style={[{ color: 'red' }]} />)
-expectType<JSX.Element>(
-  <div style={[{ color: 'red' }, [{ fontSize: '1em' }]]} />
-)
-
-// @ts-expect-error unknown prop
-expectError(<div foo="bar" />)
-
-// allow key/ref on arbitrary element
-expectType<JSX.Element>(<div key="foo" />)
-expectType<JSX.Element>(<div ref="bar" />)
-
-// allow Ref type type on arbitrary element
-const fooRef = ref<HTMLElement>()
-expectType<JSX.Element>(<div ref={fooRef} />)
-expectType<JSX.Element>(
-  <div
-    ref={el => {
-      fooRef.value = el as HTMLElement
-    }}
-  />
-)
-
-expectType<JSX.Element>(
-  <input
-    onInput={e => {
-      // infer correct event type
-      expectType<EventTarget | null>(e.target)
-    }}
-  />
-)
-
-const Foo = defineComponent({
-  props: {
-    foo: String,
-    bar: {
-      type: Number,
-      required: true
-    }
-  }
-})
-
-// @ts-expect-error
-;<Foo />
-// @ts-expect-error
-;<Foo bar="1" />
-// @ts-expect-error
-;<Foo bar={1} foo={2} />
-
-// working
-;<Foo bar={1} />
-;<Foo bar={1} foo="baz" />
-;<div slot="x" />
-
-export default ({ data }: RenderContext) => {
-  return <button {...data} />
-}
diff --git a/types/test/v3/watch-test.ts b/types/test/v3/watch-test.ts
deleted file mode 100644
index aeb5ff36c36..00000000000
--- a/types/test/v3/watch-test.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { ref, computed, watch, shallowRef } from '../../index'
-import { expectType } from '../utils'
-
-const source = ref('foo')
-const source2 = computed(() => source.value)
-const source3 = () => 1
-
-// lazy watcher will have consistent types for oldValue.
-watch(source, (value, oldValue) => {
-  expectType<string>(value)
-  expectType<string>(oldValue)
-})
-
-watch([source, source2, source3], (values, oldValues) => {
-  expectType<[string, string, number]>(values)
-  expectType<[string, string, number]>(oldValues)
-})
-
-// const array
-watch([source, source2, source3] as const, (values, oldValues) => {
-  expectType<Readonly<[string, string, number]>>(values)
-  expectType<Readonly<[string, string, number]>>(oldValues)
-})
-
-// immediate watcher's oldValue will be undefined on first run.
-watch(
-  source,
-  (value, oldValue) => {
-    expectType<string>(value)
-    expectType<string | undefined>(oldValue)
-  },
-  { immediate: true }
-)
-
-watch(
-  [source, source2, source3],
-  (values, oldValues) => {
-    expectType<[string, string, number]>(values)
-    expectType<[string | undefined, string | undefined, number | undefined]>(
-      oldValues
-    )
-  },
-  { immediate: true }
-)
-
-// const array
-watch(
-  [source, source2, source3] as const,
-  (values, oldValues) => {
-    expectType<Readonly<[string, string, number]>>(values)
-    expectType<
-      Readonly<[string | undefined, string | undefined, number | undefined]>
-    >(oldValues)
-  },
-  { immediate: true }
-)
-
-// should provide correct ref.value inner type to callbacks
-const nestedRefSource = ref({
-  foo: ref(1)
-})
-
-watch(nestedRefSource, (v, ov) => {
-  expectType<{ foo: number }>(v)
-  expectType<{ foo: number }>(ov)
-})
-
-const someRef = ref({ test: 'test' })
-const otherRef = ref({ a: 'b' })
-watch([someRef, otherRef], values => {
-  const value1 = values[0]
-  // no type error
-  console.log(value1.test)
-
-  const value2 = values[1]
-  // no type error
-  console.log(value2.a)
-})
-
-{
-  //#12978
-  type Steps = { step: '1' } | { step: '2' }
-  const shallowUnionGenParam = shallowRef<Steps>({ step: '1' })
-  const shallowUnionAsCast = shallowRef({ step: '1' } as Steps)
-
-  watch(shallowUnionGenParam, value => {
-    expectType<Steps>(value)
-  })
-  watch(shallowUnionAsCast, value => {
-    expectType<Steps>(value)
-  })
-}
\ No newline at end of file
diff --git a/types/test/vue-test.ts b/types/test/vue-test.ts
index fdf37b52abb..9ddab32dbfb 100644
--- a/types/test/vue-test.ts
+++ b/types/test/vue-test.ts
@@ -1,285 +1,91 @@
-import Vue, { VNode, defineComponent } from '../index'
-import { ComponentOptions } from '../options'
+import Vue = require("../index");
 
 class Test extends Vue {
-  a: number = 0
-
   testProperties() {
-    this.$data
-    this.$el
-    this.$options
-    this.$parent
-    this.$root
-    this.$children
-    this.$refs
-    this.$slots
-    this.$isServer
-    this.$ssrContext
-    this.$vnode
-    this.$root.$children[0].$children[0]
+    this.$data;
+    this.$el;
+    this.$options;
+    this.$parent;
+    this.$root;
+    this.$children;
+    this.$refs;
   }
 
   // test property reification
-  $el!: HTMLElement | SVGElement
-  $refs!: {
-    vue: Vue
-    element: HTMLInputElement
-    vues: Vue[]
-    elements: HTMLInputElement[]
+  $refs: {
+    vue: Vue;
+    vues: Vue[];
+  }
+  $els: {
+    element: HTMLInputElement;
+    elements: HTMLInputElement[];
   }
   testReification() {
-    this.$refs.vue.$data
-    this.$refs.element.value
-    this.$refs.vues[0].$data
-    this.$refs.elements[0].value
+    this.$refs.vue.$data;
+    this.$refs.vues[0].$data;
+
+    this.$els.element.value;
+    this.$els.elements[0].value;
   }
 
   testMethods() {
-    this.$mount('#app', false)
-    this.$forceUpdate()
-    this.$destroy()
-    this.$set({}, 'key', 'value')
-    this.$delete({}, 'key')
-    this.$watch('a', (val: number, oldVal: number) => {}, {
+    this.$watch("a", (val: number, oldVal: number) => {}, {
       immediate: true,
       deep: false
-    })()
-    this.$watch(
-      () => this.a,
-      (val: number) => {}
-    )
-    this.$on('', () => {})
-    this.$once('', () => {})
-    this.$off('', () => {})
-    this.$emit('', 1, 2, 3)
-    this.$nextTick(function () {
-      this.$nextTick
-    })
-    this.$nextTick().then(() => {})
-    this.$createElement('div', {}, 'message')
+    })();
+    this.$watch(() => {}, (val: number) => {});
+    this.$get("");
+    this.$set("key", "value");
+    this.$delete("key");
+    this.$eval("");
+    this.$interpolate("");
+    this.$log("");
+
+    this.$on("", () => {});
+    this.$once("", () => {});
+    this.$off("", () => {});
+    this.$emit("", 1, 2, 3);
+    this.$broadcast("", 1, 2, 3);
+    this.$dispatch("", 1, 2, 3);
+
+    this.$appendTo("", () => {});
+    this.$before("", () => {});
+    this.$after("", () => {});
+    this.$remove(() => {});
+    this.$nextTick(function() {
+      this.$nextTick;
+    });
+
+    this.$mount("#app");
+    this.$destroy(true);
   }
 
   static testConfig() {
-    const { config } = this
-    config.silent
-    config.optionMergeStrategies
-    config.devtools
-    config.errorHandler = (err, vm) => {
-      if (vm instanceof Test) {
-        vm.testProperties()
-        vm.testMethods()
-      }
-    }
-    config.warnHandler = (msg, vm) => {
-      if (vm instanceof Test) {
-        vm.testProperties()
-        vm.testMethods()
-      }
-    }
-    config.keyCodes = { esc: 27 }
-    config.ignoredElements = ['foo', /^ion-/]
-    config.async = false
+    const { config } = this;
+    config.debug;
+    config.delimiters;
+    config.unsafeDelimiters;
+    config.silent;
+    config.async;
+    config.devtools;
   }
 
   static testMethods() {
     this.extend({
       data() {
         return {
-          msg: ''
-        }
+          msg: ""
+        };
       }
-    })
-    this.nextTick(() => {})
-    this.nextTick(
-      function () {
-        console.log(this.text === 'test')
-      },
-      { text: 'test' }
-    )
-    this.nextTick().then(() => {})
-    this.set({}, '', '')
-    this.set({}, 1, '')
-    this.set([true, false, true], 1, true)
-    this.delete({}, '')
-    this.delete({}, 1)
-    this.delete([true, false], 0)
-    this.directive('', { bind() {} })
-    this.filter('', (value: number) => value)
-    this.component('', { data: () => ({}) })
-    this.component('', {
-      functional: true,
-      render(h) {
-        return h('div', 'hello!')
-      }
-    })
-    this.use
-    this.mixin(Test)
-    this.compile('<div>{{ message }}</div>')
-    this.use(() => {})
-      .use(() => {})
-      .mixin({})
-      .mixin({})
+    });
+    this.nextTick(() => {});
+    this.set({}, "", "");
+    this.delete({}, "");
+    this.directive("", {bind() {}});
+    this.elementDirective("", {bind() {}});
+    this.filter("", (value: number) => value);
+    this.component("", { data: () => ({}) });
+    this.use;
+    this.mixin(Test);
   }
 }
-
-const HelloWorldComponent = Vue.extend({
-  props: ['name'],
-  data() {
-    return {
-      message: 'Hello ' + this.name
-    }
-  },
-  computed: {
-    shouted(): string {
-      return this.message.toUpperCase()
-    }
-  },
-  methods: {
-    getMoreExcited() {
-      this.message += '!'
-    }
-  },
-  watch: {
-    message(a: string) {
-      console.log(`Message ${this.message} was changed!`)
-    }
-  }
-})
-
-const FunctionalHelloWorldComponent = Vue.extend({
-  functional: true,
-  props: ['name'],
-  render(createElement, ctxt) {
-    return createElement('div', 'Hello ' + ctxt.props.name)
-  }
-})
-
-const FunctionalScopedSlotsComponent = Vue.extend({
-  functional: true,
-  render(h, ctx) {
-    return (
-      (ctx.scopedSlots.default && ctx.scopedSlots.default({})) ||
-      h('div', 'functional scoped slots')
-    )
-  }
-})
-
-const Parent = Vue.extend({
-  data() {
-    return { greeting: 'Hello' }
-  }
-})
-
-const Child = Parent.extend({
-  methods: {
-    foo() {
-      console.log(this.greeting.toLowerCase())
-    }
-  }
-})
-
-const GrandChild = Child.extend({
-  computed: {
-    lower(): string {
-      return this.greeting.toLowerCase()
-    }
-  }
-})
-
-new GrandChild().lower.toUpperCase()
-for (let _ in new Test().$options) {
-}
-declare const options: ComponentOptions<Vue>
-Vue.extend(options)
-Vue.component('test-comp', options)
-new Vue(options)
-
-// cyclic example
-Vue.extend({
-  props: {
-    bar: {
-      type: String
-    }
-  },
-  methods: {
-    foo() {}
-  },
-  mounted() {
-    this.foo()
-  },
-  // manual annotation
-  render(h): VNode {
-    const a = this.bar
-    return h('canvas', {}, [a])
-  }
-})
-
-declare function decorate<VC extends typeof Vue>(v: VC): VC
-
-@decorate
-class Decorated extends Vue {
-  a = 123
-}
-
-const obj = Vue.observable({ a: 1 })
-obj.a++
-
-// VNodeData style tests.
-const ComponentWithStyleInVNodeData = Vue.extend({
-  render(h) {
-    const elementWithStyleAsString = h('div', {
-      style: '--theme-color: black;'
-    })
-
-    const elementWithStyleCSSProperties = h('div', {
-      style: { ['--theme-color' as any]: 'black' }
-    })
-
-    const elementWithStyleAsArrayOfStyleValues = h('div', {
-      style: [{ ['--theme-color' as any]: 'black' }]
-    })
-
-    return h('div', undefined, [
-      elementWithStyleAsString,
-      elementWithStyleCSSProperties,
-      elementWithStyleAsArrayOfStyleValues
-    ])
-  }
-})
-
-// infer mixin type with new Vue() #12730
-new Vue({
-  mixins: [
-    defineComponent({
-      props: {
-        p1: String,
-        p2: {
-          type: Number,
-          default: 0
-        }
-      },
-      data() {
-        return {
-          foo: 123
-        }
-      },
-      computed: {
-        bar() {
-          return 123
-        }
-      }
-    }),
-    {
-      methods: {
-        hello(n: number) {}
-      }
-    }
-  ],
-  created() {
-    this.hello(this.foo)
-    this.hello(this.bar)
-    // @ts-expect-error
-    this.hello(this.p1)
-    this.hello(this.p2)
-  }
-})
diff --git a/types/tsconfig.json b/types/tsconfig.json
deleted file mode 100644
index 36c5afee057..00000000000
--- a/types/tsconfig.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "compilerOptions": {
-    "target": "esnext",
-    "experimentalDecorators": true,
-    "lib": ["dom", "esnext"],
-    "types": ["node"],
-    "module": "esnext",
-    "moduleResolution": "node",
-    "jsx": "preserve",
-    "strict": true,
-    "noEmit": true,
-    "paths": {
-      "vue": ["../index.d.ts"]
-    }
-  },
-  "include": ["."],
-  "compileOnSave": false
-}
diff --git a/types/umd.d.ts b/types/umd.d.ts
deleted file mode 100644
index 3df7afef21c..00000000000
--- a/types/umd.d.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import * as V from './index'
-import {
-  DefaultData,
-  DefaultProps,
-  DefaultMethods,
-  DefaultComputed,
-  PropsDefinition
-} from './options'
-
-// Expose some types for backward compatibility...
-declare namespace Vue {
-  // vue.d.ts
-  export type CreateElement = V.CreateElement
-  export type VueConstructor<V extends Vue = Vue> = V.VueConstructor<V>
-
-  // options.d.ts
-  export type Component<
-    Data = DefaultData<never>,
-    Methods = DefaultMethods<never>,
-    Computed = DefaultComputed,
-    Props = DefaultProps
-  > = V.Component<Data, Methods, Computed, Props>
-  export type AsyncComponent<
-    Data = DefaultData<never>,
-    Methods = DefaultMethods<never>,
-    Computed = DefaultComputed,
-    Props = DefaultProps
-  > = V.AsyncComponent<Data, Methods, Computed, Props>
-  export type ComponentOptions<
-    V extends Vue,
-    Data = DefaultData<V>,
-    Methods = DefaultMethods<V>,
-    Computed = DefaultComputed,
-    PropsDef = PropsDefinition<DefaultProps>,
-    Props = DefaultProps
-  > = V.ComponentOptions<V, Data, Methods, Computed, PropsDef, Props>
-  export type FunctionalComponentOptions<
-    Props = DefaultProps,
-    PropDefs = PropsDefinition<Props>
-  > = V.FunctionalComponentOptions<Props, PropDefs>
-  export type RenderContext<Props = DefaultProps> = V.RenderContext<Props>
-  export type PropType<T> = V.PropType<T>
-  export type PropOptions<T = any> = V.PropOptions<T>
-  export type ComputedOptions<T> = V.ComputedOptions<T>
-  export type WatchHandler<T> = V.WatchHandler<T>
-  export type WatchOptions = V.WatchOptions
-  export type WatchOptionsWithHandler<T> = V.WatchOptionsWithHandler<T>
-  export type DirectiveFunction = V.DirectiveFunction
-  export type DirectiveOptions = V.DirectiveOptions
-
-  // plugin.d.ts
-  export type PluginFunction<T> = V.PluginFunction<T>
-  export type PluginObject<T> = V.PluginObject<T>
-
-  // vnode.d.ts
-  export type VNodeChildren = V.VNodeChildren
-  export type VNodeChildrenArrayContents = V.VNodeChildrenArrayContents
-  export type VNode = V.VNode
-  export type VNodeComponentOptions = V.VNodeComponentOptions
-  export type VNodeData = V.VNodeData
-  export type VNodeDirective = V.VNodeDirective
-}
-
-declare class Vue extends V.default {}
-
-export = Vue
-
-export as namespace Vue
diff --git a/types/v3-component-options.d.ts b/types/v3-component-options.d.ts
deleted file mode 100644
index e2da34e753f..00000000000
--- a/types/v3-component-options.d.ts
+++ /dev/null
@@ -1,252 +0,0 @@
-import { Vue } from './vue'
-import { VNode } from './vnode'
-import { ComponentOptions as Vue2ComponentOptions } from './options'
-import { EmitsOptions, SetupContext } from './v3-setup-context'
-import { Data, LooseRequired, UnionToIntersection } from './common'
-import {
-  ComponentPropsOptions,
-  ExtractDefaultPropTypes,
-  ExtractPropTypes
-} from './v3-component-props'
-import { CreateComponentPublicInstance } from './v3-component-public-instance'
-export { ComponentPropsOptions } from './v3-component-props'
-
-/**
- * Interface for declaring custom options.
- *
- * @example
- * ```ts
- * declare module 'vue' {
- *   interface ComponentCustomOptions {
- *     beforeRouteUpdate?(
- *       to: Route,
- *       from: Route,
- *       next: () => void
- *     ): void
- *   }
- * }
- * ```
- */
-export interface ComponentCustomOptions {}
-
-export type ComputedGetter<T> = (ctx?: any) => T
-export type ComputedSetter<T> = (v: T) => void
-
-export interface WritableComputedOptions<T> {
-  get: ComputedGetter<T>
-  set: ComputedSetter<T>
-}
-
-export type ComputedOptions = Record<
-  string,
-  ComputedGetter<any> | WritableComputedOptions<any>
->
-
-export interface MethodOptions {
-  [key: string]: Function
-}
-
-export type SetupFunction<
-  Props,
-  RawBindings = {},
-  Emits extends EmitsOptions = {}
-> = (
-  this: void,
-  props: Readonly<Props>,
-  ctx: SetupContext<Emits>
-) => RawBindings | (() => VNode | null) | void
-
-type ExtractOptionProp<T> = T extends ComponentOptionsBase<
-  infer P, // Props
-  any, // RawBindings
-  any, // D
-  any, // C
-  any, // M
-  any, // Mixin
-  any, // Extends
-  any, // EmitsOptions
-  any // Defaults
->
-  ? unknown extends P
-    ? {}
-    : P
-  : {}
-
-export interface ComponentOptionsBase<
-  Props,
-  RawBindings,
-  D,
-  C extends ComputedOptions,
-  M extends MethodOptions,
-  Mixin extends ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin,
-  Emits extends EmitsOptions,
-  EmitNames extends string = string,
-  Defaults = {}
-> extends Omit<
-      Vue2ComponentOptions<Vue, D, M, C, Props>,
-      'data' | 'computed' | 'methods' | 'setup' | 'props' | 'mixins' | 'extends'
-    >,
-    ComponentCustomOptions {
-  // allow any options
-  [key: string]: any
-
-  // rewrite options api types
-  data?: (
-    this: CreateComponentPublicInstance<Props, {}, {}, {}, M, Mixin, Extends>,
-    vm: CreateComponentPublicInstance<Props, {}, {}, {}, M, Mixin, Extends>
-  ) => D
-  computed?: C
-  methods?: M
-  mixins?: Mixin[]
-  extends?: Extends
-  emits?: (Emits | EmitNames[]) & ThisType<void>
-  setup?: SetupFunction<
-    Readonly<
-      LooseRequired<
-        Props &
-          UnionToIntersection<ExtractOptionProp<Mixin>> &
-          UnionToIntersection<ExtractOptionProp<Extends>>
-      >
-    >,
-    RawBindings,
-    Emits
-  >
-
-  __defaults?: Defaults
-}
-
-export type ComponentOptionsMixin = ComponentOptionsBase<
-  any,
-  any,
-  any,
-  any,
-  any,
-  any,
-  any,
-  any,
-  any,
-  any
->
-
-export type ExtractComputedReturns<T extends any> = {
-  [key in keyof T]: T[key] extends { get: (...args: any[]) => infer TReturn }
-    ? TReturn
-    : T[key] extends (...args: any[]) => infer TReturn
-    ? TReturn
-    : never
-}
-
-export type ComponentOptionsWithProps<
-  PropsOptions = ComponentPropsOptions,
-  RawBindings = Data,
-  D = Data,
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Emits extends EmitsOptions = {},
-  EmitsNames extends string = string,
-  Props = ExtractPropTypes<PropsOptions>,
-  Defaults = ExtractDefaultPropTypes<PropsOptions>
-> = ComponentOptionsBase<
-  Props,
-  RawBindings,
-  D,
-  C,
-  M,
-  Mixin,
-  Extends,
-  Emits,
-  EmitsNames,
-  Defaults
-> & {
-  props?: PropsOptions
-} & ThisType<
-    CreateComponentPublicInstance<
-      Props,
-      RawBindings,
-      D,
-      C,
-      M,
-      Mixin,
-      Extends,
-      Emits
-    >
-  >
-
-export type ComponentOptionsWithArrayProps<
-  PropNames extends string = string,
-  RawBindings = Data,
-  D = Data,
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Emits extends EmitsOptions = {},
-  EmitsNames extends string = string,
-  Props = Readonly<{ [key in PropNames]?: any }>
-> = ComponentOptionsBase<
-  Props,
-  RawBindings,
-  D,
-  C,
-  M,
-  Mixin,
-  Extends,
-  Emits,
-  EmitsNames,
-  {}
-> & {
-  props?: PropNames[]
-} & ThisType<
-    CreateComponentPublicInstance<
-      Props,
-      RawBindings,
-      D,
-      C,
-      M,
-      Mixin,
-      Extends,
-      Emits
-    >
-  >
-
-export type ComponentOptionsWithoutProps<
-  Props = {},
-  RawBindings = Data,
-  D = Data,
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Emits extends EmitsOptions = {},
-  EmitsNames extends string = string
-> = ComponentOptionsBase<
-  Props,
-  RawBindings,
-  D,
-  C,
-  M,
-  Mixin,
-  Extends,
-  Emits,
-  EmitsNames,
-  {}
-> & {
-  props?: undefined
-} & ThisType<
-    CreateComponentPublicInstance<
-      Props,
-      RawBindings,
-      D,
-      C,
-      M,
-      Mixin,
-      Extends,
-      Emits
-    >
-  >
-
-export type WithLegacyAPI<T, D, C, M, Props> = T &
-  Omit<Vue2ComponentOptions<Vue, D, M, C, Props>, keyof T>
diff --git a/types/v3-component-props.d.ts b/types/v3-component-props.d.ts
deleted file mode 100644
index f0b2b706082..00000000000
--- a/types/v3-component-props.d.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-import { Data, IfAny } from './common'
-
-export type ComponentPropsOptions<P = Data> =
-  | ComponentObjectPropsOptions<P>
-  | string[]
-
-export type ComponentObjectPropsOptions<P = Data> = {
-  [K in keyof P]: Prop<P[K]> | null
-}
-
-export type Prop<T, D = T> = PropOptions<T, D> | PropType<T>
-
-type DefaultFactory<T> = () => T | null | undefined
-
-export interface PropOptions<T = any, D = T> {
-  type?: PropType<T> | true | null
-  required?: boolean
-  default?: D | DefaultFactory<D> | null | undefined | object
-  validator?(value: unknown): boolean
-}
-
-export type PropType<T> = PropConstructor<T> | PropConstructor<T>[]
-
-type PropConstructor<T> =
-  | { (): T }
-  | { new (...args: never[]): T & object }
-  | { new (...args: string[]): Function }
-
-type RequiredKeys<T> = {
-  [K in keyof T]: T[K] extends
-    | { required: true }
-    | { default: any }
-    | BooleanConstructor
-    | { type: BooleanConstructor }
-    ? K
-    : never
-}[keyof T]
-
-type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>
-
-type ExtractFunctionPropType<
-  T extends Function,
-  TArgs extends Array<any> = any[],
-  TResult = any
-> = T extends (...args: TArgs) => TResult ? T : never
-
-type ExtractCorrectPropType<T> = T extends Function
-  ? ExtractFunctionPropType<T>
-  : Exclude<T, Function>
-
-type InferPropType<T> = [T] extends [null]
-  ? any // null & true would fail to infer
-  : [T] extends [{ type: null | true }]
-  ? any // As TS issue https://github.com/Microsoft/TypeScript/issues/14829 // somehow `ObjectConstructor` when inferred from { (): T } becomes `any` // `BooleanConstructor` when inferred from PropConstructor(with PropMethod) becomes `Boolean`
-  : [T] extends [ObjectConstructor | { type: ObjectConstructor }]
-  ? Record<string, any>
-  : [T] extends [BooleanConstructor | { type: BooleanConstructor }]
-  ? boolean
-  : [T] extends [DateConstructor | { type: DateConstructor }]
-  ? Date
-  : [T] extends [(infer U)[] | { type: (infer U)[] }]
-  ? U extends DateConstructor
-    ? Date | InferPropType<U>
-    : InferPropType<U>
-  : [T] extends [Prop<infer V, infer D>]
-  ? unknown extends V
-    ? IfAny<V, V, D>
-    : V
-  : T
-
-export type ExtractPropTypes<O> = {
-  // use `keyof Pick<O, RequiredKeys<O>>` instead of `RequiredKeys<O>` to support IDE features
-  [K in keyof Pick<O, RequiredKeys<O>>]: InferPropType<O[K]>
-} & {
-  // use `keyof Pick<O, OptionalKeys<O>>` instead of `OptionalKeys<O>` to support IDE features
-  [K in keyof Pick<O, OptionalKeys<O>>]?: InferPropType<O[K]>
-}
-
-type DefaultKeys<T> = {
-  [K in keyof T]: T[K] extends
-    | {
-        default: any
-      }
-    | BooleanConstructor
-    | { type: BooleanConstructor }
-    ? T[K] extends {
-        type: BooleanConstructor
-        required: true
-      }
-      ? never
-      : K
-    : never
-}[keyof T]
-
-// extract props which defined with default from prop options
-export type ExtractDefaultPropTypes<O> = O extends object
-  ? // use `keyof Pick<O, DefaultKeys<O>>` instead of `DefaultKeys<O>` to support IDE features
-    { [K in keyof Pick<O, DefaultKeys<O>>]: InferPropType<O[K]> }
-  : {}
diff --git a/types/v3-component-public-instance.d.ts b/types/v3-component-public-instance.d.ts
deleted file mode 100644
index 1c55908ac73..00000000000
--- a/types/v3-component-public-instance.d.ts
+++ /dev/null
@@ -1,232 +0,0 @@
-import {
-  DebuggerEvent,
-  ShallowUnwrapRef,
-  UnwrapNestedRefs
-} from './v3-generated'
-import { UnionToIntersection } from './common'
-
-import { Vue, VueConstructor } from './vue'
-import {
-  ComputedOptions,
-  MethodOptions,
-  ExtractComputedReturns,
-  ComponentOptionsMixin,
-  ComponentOptionsBase
-} from './v3-component-options'
-import { EmitFn, EmitsOptions } from './v3-setup-context'
-
-/**
- * Custom properties added to component instances in any way and can be accessed through `this`
- *
- * @example
- * ```ts
- * import { Router } from 'vue-router'
- *
- * declare module 'vue' {
- *   interface ComponentCustomProperties {
- *     $router: Router
- *   }
- * }
- * ```
- */
-export interface ComponentCustomProperties {}
-
-export type ComponentInstance = InstanceType<VueConstructor>
-
-export type OptionTypesKeys = 'P' | 'B' | 'D' | 'C' | 'M' | 'Defaults'
-
-export type OptionTypesType<
-  P = {},
-  B = {},
-  D = {},
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Defaults = {}
-> = {
-  P: P
-  B: B
-  D: D
-  C: C
-  M: M
-  Defaults: Defaults
-}
-
-type IsDefaultMixinComponent<T> = T extends ComponentOptionsMixin
-  ? ComponentOptionsMixin extends T
-    ? true
-    : false
-  : false
-
-type MixinToOptionTypes<T> = T extends ComponentOptionsBase<
-  infer P,
-  infer B,
-  infer D,
-  infer C,
-  infer M,
-  infer Mixin,
-  infer Extends,
-  any,
-  any,
-  infer Defaults
->
-  ? OptionTypesType<P & {}, B & {}, D & {}, C & {}, M & {}, Defaults & {}> &
-      IntersectionMixin<Mixin> &
-      IntersectionMixin<Extends>
-  : never
-
-// ExtractMixin(map type) is used to resolve circularly references
-type ExtractMixin<T> = {
-  Mixin: MixinToOptionTypes<T>
-}[T extends ComponentOptionsMixin ? 'Mixin' : never]
-
-export type IntersectionMixin<T> = IsDefaultMixinComponent<T> extends true
-  ? OptionTypesType<{}, {}, {}, {}, {}, {}>
-  : UnionToIntersection<ExtractMixin<T>>
-
-export type UnwrapMixinsType<
-  T,
-  Type extends OptionTypesKeys
-> = T extends OptionTypesType ? T[Type] : never
-
-type EnsureNonVoid<T> = T extends void ? {} : T
-
-export type CreateComponentPublicInstance<
-  P = {},
-  B = {},
-  D = {},
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = {},
-  PublicProps = P,
-  Defaults = {},
-  MakeDefaultsOptional extends boolean = false,
-  PublicMixin = IntersectionMixin<Mixin> & IntersectionMixin<Extends>,
-  PublicP = UnwrapMixinsType<PublicMixin, 'P'> & EnsureNonVoid<P>,
-  PublicB = UnwrapMixinsType<PublicMixin, 'B'> & EnsureNonVoid<B>,
-  PublicD = UnwrapMixinsType<PublicMixin, 'D'> & EnsureNonVoid<D>,
-  PublicC extends ComputedOptions = UnwrapMixinsType<PublicMixin, 'C'> &
-    EnsureNonVoid<C>,
-  PublicM extends MethodOptions = UnwrapMixinsType<PublicMixin, 'M'> &
-    EnsureNonVoid<M>,
-  PublicDefaults = UnwrapMixinsType<PublicMixin, 'Defaults'> &
-    EnsureNonVoid<Defaults>
-> = ComponentPublicInstance<
-  PublicP,
-  PublicB,
-  PublicD,
-  PublicC,
-  PublicM,
-  E,
-  PublicProps,
-  PublicDefaults,
-  MakeDefaultsOptional
->
-
-// public properties exposed on the proxy, which is used as the render context
-// in templates (as `this` in the render option)
-export type ComponentPublicInstance<
-  P = {}, // props type extracted from props option
-  B = {}, // raw bindings returned from setup()
-  D = {}, // return from data()
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  E extends EmitsOptions = {},
-  PublicProps = P,
-  Defaults = {},
-  MakeDefaultsOptional extends boolean = false,
-  Options = ComponentOptionsBase<
-    any,
-    any,
-    any,
-    any,
-    any,
-    any,
-    any,
-    any,
-    any,
-    any
-  >
-> = Vue3Instance<
-  D,
-  P,
-  PublicProps,
-  E,
-  Defaults,
-  MakeDefaultsOptional,
-  Options
-> &
-  Readonly<P> &
-  ShallowUnwrapRef<B> &
-  UnwrapNestedRefs<D> &
-  ExtractComputedReturns<C> &
-  M &
-  ComponentCustomProperties
-
-interface Vue3Instance<
-  D,
-  P,
-  PublicProps,
-  E,
-  Defaults,
-  MakeDefaultsOptional,
-  Options
-> extends Vue<
-    D,
-    Readonly<
-      MakeDefaultsOptional extends true
-        ? Partial<Defaults> & Omit<P & PublicProps, keyof Defaults>
-        : P & PublicProps
-    >,
-    ComponentPublicInstance,
-    Options & MergedComponentOptionsOverride,
-    EmitFn<E>
-  > {}
-
-type MergedHook<T = () => void> = T | T[]
-
-export type MergedComponentOptionsOverride = {
-  beforeCreate?: MergedHook
-  created?: MergedHook
-  beforeMount?: MergedHook
-  mounted?: MergedHook
-  beforeUpdate?: MergedHook
-  updated?: MergedHook
-  activated?: MergedHook
-  deactivated?: MergedHook
-  /** @deprecated use `beforeUnmount` instead */
-  beforeDestroy?: MergedHook
-  beforeUnmount?: MergedHook
-  /** @deprecated use `unmounted` instead */
-  destroyed?: MergedHook
-  unmounted?: MergedHook
-  renderTracked?: MergedHook<DebuggerHook>
-  renderTriggered?: MergedHook<DebuggerHook>
-  errorCaptured?: MergedHook<ErrorCapturedHook>
-}
-
-export type DebuggerHook = (e: DebuggerEvent) => void
-
-export type ErrorCapturedHook<TError = unknown> = (
-  err: TError,
-  instance: ComponentPublicInstance | null,
-  info: string
-) => boolean | void
-
-export type ComponentPublicInstanceConstructor<
-  T extends ComponentPublicInstance<
-    Props,
-    RawBindings,
-    D,
-    C,
-    M
-  > = ComponentPublicInstance<any, any, any>,
-  Props = any,
-  RawBindings = any,
-  D = any,
-  C extends ComputedOptions = ComputedOptions,
-  M extends MethodOptions = MethodOptions
-> = {
-  new (...args: any[]): T
-}
diff --git a/types/v3-define-async-component.d.ts b/types/v3-define-async-component.d.ts
deleted file mode 100644
index 8648ef6229f..00000000000
--- a/types/v3-define-async-component.d.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { AsyncComponent, Component } from './options'
-
-export type AsyncComponentResolveResult<T = Component> = T | { default: T } // es modules
-
-export type AsyncComponentLoader<T = any> = () => Promise<
-  AsyncComponentResolveResult<T>
->
-
-export interface AsyncComponentOptions {
-  loader: AsyncComponentLoader
-  loadingComponent?: Component
-  errorComponent?: Component
-  delay?: number
-  timeout?: number
-  // suspensible?: boolean
-  onError?: (
-    error: Error,
-    retry: () => void,
-    fail: () => void,
-    attempts: number
-  ) => any
-}
-
-export function defineAsyncComponent(
-  source: AsyncComponentLoader | AsyncComponentOptions
-): AsyncComponent
diff --git a/types/v3-define-component.d.ts b/types/v3-define-component.d.ts
deleted file mode 100644
index 03ef52d1856..00000000000
--- a/types/v3-define-component.d.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-import {
-  ComponentPropsOptions,
-  ExtractDefaultPropTypes,
-  ExtractPropTypes
-} from './v3-component-props'
-import {
-  MethodOptions,
-  ComputedOptions,
-  ComponentOptionsWithoutProps,
-  ComponentOptionsWithArrayProps,
-  ComponentOptionsWithProps,
-  ComponentOptionsMixin,
-  ComponentOptionsBase
-} from './v3-component-options'
-import {
-  ComponentPublicInstanceConstructor,
-  CreateComponentPublicInstance
-} from './v3-component-public-instance'
-import { Data, HasDefined } from './common'
-import { EmitsOptions } from './v3-setup-context'
-import { CreateElement, RenderContext } from './umd'
-
-export type DefineComponent<
-  PropsOrPropOptions = {},
-  RawBindings = {},
-  D = {},
-  C extends ComputedOptions = ComputedOptions,
-  M extends MethodOptions = MethodOptions,
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  E extends EmitsOptions = {},
-  EE extends string = string,
-  Props = Readonly<
-    PropsOrPropOptions extends ComponentPropsOptions
-      ? ExtractPropTypes<PropsOrPropOptions>
-      : PropsOrPropOptions
-  >,
-  Defaults = ExtractDefaultPropTypes<PropsOrPropOptions>
-> = ComponentPublicInstanceConstructor<
-  CreateComponentPublicInstance<
-    Props,
-    RawBindings,
-    D,
-    C,
-    M,
-    Mixin,
-    Extends,
-    E,
-    Props,
-    Defaults,
-    true
-  > &
-    Props
-> &
-  ComponentOptionsBase<
-    Props,
-    RawBindings,
-    D,
-    C,
-    M,
-    Mixin,
-    Extends,
-    E,
-    EE,
-    Defaults
-  > & {
-    props: PropsOrPropOptions
-  }
-
-/**
- * overload 1: object format with no props
- */
-export function defineComponent<
-  RawBindings,
-  D = {},
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Emits extends EmitsOptions = {},
-  EmitsNames extends string = string
->(
-  options: { functional?: never } & ComponentOptionsWithoutProps<
-    {},
-    RawBindings,
-    D,
-    C,
-    M,
-    Mixin,
-    Extends,
-    Emits,
-    EmitsNames
-  >
-): DefineComponent<{}, RawBindings, D, C, M, Mixin, Extends, Emits>
-
-/**
- * overload 2: object format with array props declaration
- * props inferred as `{ [key in PropNames]?: any }`
- *
- * return type is for Vetur and TSX support
- */
-export function defineComponent<
-  PropNames extends string,
-  RawBindings = {},
-  D = {},
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Emits extends EmitsOptions = {},
-  EmitsNames extends string = string,
-  PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
->(
-  options: { functional?: never } & ComponentOptionsWithArrayProps<
-    PropNames,
-    RawBindings,
-    D,
-    C,
-    M,
-    Mixin,
-    Extends,
-    Emits,
-    EmitsNames
-  >
-): DefineComponent<
-  Readonly<{ [key in PropNames]?: any }>,
-  RawBindings,
-  D,
-  C,
-  M,
-  Mixin,
-  Extends,
-  Emits
->
-
-/**
- * overload 3: object format with object props declaration
- *
- * see `ExtractPropTypes` in './componentProps.ts'
- */
-export function defineComponent<
-  Props,
-  RawBindings = {},
-  D = {},
-  C extends ComputedOptions = {},
-  M extends MethodOptions = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Emits extends EmitsOptions = {},
-  EmitsNames extends string = string,
-  PropsOptions extends ComponentPropsOptions = ComponentPropsOptions
->(
-  options: HasDefined<Props> extends true
-    ? { functional?: never } & ComponentOptionsWithProps<
-        PropsOptions,
-        RawBindings,
-        D,
-        C,
-        M,
-        Mixin,
-        Extends,
-        Emits,
-        EmitsNames,
-        Props
-      >
-    : { functional?: never } & ComponentOptionsWithProps<
-        PropsOptions,
-        RawBindings,
-        D,
-        C,
-        M,
-        Mixin,
-        Extends,
-        Emits,
-        EmitsNames
-      >
-): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, Emits>
-
-/**
- * overload 4.1: functional component with array props
- */
-export function defineComponent<
-  PropNames extends string,
-  Props = Readonly<{ [key in PropNames]?: any }>
->(options: {
-  functional: true
-  props?: PropNames[]
-  render?: (h: CreateElement, context: RenderContext<Props>) => any
-}): DefineComponent<Props>
-
-/**
- * overload 4.2: functional component with object props
- */
-export function defineComponent<
-  PropsOptions extends ComponentPropsOptions = ComponentPropsOptions,
-  Props = ExtractPropTypes<PropsOptions>
->(options: {
-  functional: true
-  props?: PropsOptions
-  render?: (h: CreateElement, context: RenderContext<Props>) => any
-}): DefineComponent<PropsOptions>
diff --git a/types/v3-directive.d.ts b/types/v3-directive.d.ts
deleted file mode 100644
index f6b091f80e9..00000000000
--- a/types/v3-directive.d.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import type { VNodeDirective, VNode } from './vnode'
-
-export type DirectiveModifiers = Record<string, boolean>
-
-export interface DirectiveBinding<V> extends Readonly<VNodeDirective> {
-  readonly modifiers: DirectiveModifiers
-  readonly value: V
-  readonly oldValue: V | null
-}
-
-export type DirectiveHook<T = any, Prev = VNode | null, V = any> = (
-  el: T,
-  binding: DirectiveBinding<V>,
-  vnode: VNode,
-  prevVNode: Prev
-) => void
-
-export interface ObjectDirective<T = any, V = any> {
-  bind?: DirectiveHook<T, any, V>
-  inserted?: DirectiveHook<T, any, V>
-  update?: DirectiveHook<T, any, V>
-  componentUpdated?: DirectiveHook<T, any, V>
-  unbind?: DirectiveHook<T, any, V>
-}
-export type FunctionDirective<T = any, V = any> = DirectiveHook<T, any, V>
-
-export type Directive<T = any, V = any> =
-  | ObjectDirective<T, V>
-  | FunctionDirective<T, V>
diff --git a/types/v3-manual-apis.d.ts b/types/v3-manual-apis.d.ts
deleted file mode 100644
index 8636c11d6b2..00000000000
--- a/types/v3-manual-apis.d.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { SetupContext } from './v3-setup-context'
-import { CreateElement, Vue } from './vue'
-
-export function getCurrentInstance(): { proxy: Vue } | null
-
-export const h: CreateElement
-
-export function useSlots(): SetupContext['slots']
-export function useAttrs(): SetupContext['attrs']
-export function useListeners(): SetupContext['listeners']
diff --git a/types/v3-setup-context.d.ts b/types/v3-setup-context.d.ts
deleted file mode 100644
index 77b49bed8a6..00000000000
--- a/types/v3-setup-context.d.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { VNode } from './vnode'
-import { Data, UnionToIntersection } from './common'
-import { Vue } from './vue'
-
-export type Slot = (...args: any[]) => VNode[]
-
-export type Slots = Record<string, Slot | undefined>
-
-export type ObjectEmitsOptions = Record<
-  string,
-  ((...args: any[]) => any) | null
->
-
-export type EmitsOptions = ObjectEmitsOptions | string[]
-
-export type EmitFn<
-  Options = ObjectEmitsOptions,
-  Event extends keyof Options = keyof Options,
-  ReturnType extends void | Vue = void
-> = Options extends Array<infer V>
-  ? (event: V, ...args: any[]) => ReturnType
-  : {} extends Options // if the emit is empty object (usually the default value for emit) should be converted to function
-  ? (event: string, ...args: any[]) => ReturnType
-  : UnionToIntersection<
-      {
-        [key in Event]: Options[key] extends (...args: infer Args) => any
-          ? (event: key, ...args: Args) => ReturnType
-          : (event: key, ...args: any[]) => ReturnType
-      }[Event]
-    >
-
-export interface SetupContext<E extends EmitsOptions = {}> {
-  attrs: Data
-  /**
-   * Equivalent of `this.$listeners`, which is Vue 2 only.
-   */
-  listeners: Record<string, Function | Function[]>
-  slots: Slots
-  emit: EmitFn<E>
-  expose(exposed?: Record<string, any>): void
-}
diff --git a/types/v3-setup-helpers.d.ts b/types/v3-setup-helpers.d.ts
deleted file mode 100644
index 165605ee51b..00000000000
--- a/types/v3-setup-helpers.d.ts
+++ /dev/null
@@ -1,154 +0,0 @@
-import { EmitFn, EmitsOptions } from './v3-setup-context'
-import {
-  ComponentObjectPropsOptions,
-  ExtractPropTypes
-} from './v3-component-props'
-
-/**
- * Vue `<script setup>` compiler macro for declaring component props. The
- * expected argument is the same as the component `props` option.
- *
- * Example runtime declaration:
- * ```js
- * // using Array syntax
- * const props = defineProps(['foo', 'bar'])
- * // using Object syntax
- * const props = defineProps({
- *   foo: String,
- *   bar: {
- *     type: Number,
- *     required: true
- *   }
- * })
- * ```
- *
- * Equivalent type-based declaration:
- * ```ts
- * // will be compiled into equivalent runtime declarations
- * const props = defineProps<{
- *   foo?: string
- *   bar: number
- * }>()
- * ```
- *
- * This is only usable inside `<script setup>`, is compiled away in the
- * output and should **not** be actually called at runtime.
- */
-// overload 1: runtime props w/ array
-export function defineProps<PropNames extends string = string>(
-  props: PropNames[]
-): Readonly<{ [key in PropNames]?: any }>
-// overload 2: runtime props w/ object
-export function defineProps<
-  PP extends ComponentObjectPropsOptions = ComponentObjectPropsOptions
->(props: PP): Readonly<ExtractPropTypes<PP>>
-// overload 3: typed-based declaration
-export function defineProps<TypeProps>(): Readonly<TypeProps>
-
-/**
- * Vue `<script setup>` compiler macro for declaring a component's emitted
- * events. The expected argument is the same as the component `emits` option.
- *
- * Example runtime declaration:
- * ```js
- * const emit = defineEmits(['change', 'update'])
- * ```
- *
- * Example type-based declaration:
- * ```ts
- * const emit = defineEmits<{
- *   (event: 'change'): void
- *   (event: 'update', id: number): void
- * }>()
- *
- * emit('change')
- * emit('update', 1)
- * ```
- *
- * This is only usable inside `<script setup>`, is compiled away in the
- * output and should **not** be actually called at runtime.
- */
-// overload 1: runtime emits w/ array
-export function defineEmits<EE extends string = string>(
-  emitOptions: EE[]
-): EmitFn<EE[]>
-export function defineEmits<E extends EmitsOptions = EmitsOptions>(
-  emitOptions: E
-): EmitFn<E>
-export function defineEmits<TypeEmit>(): TypeEmit
-
-/**
- * Vue `<script setup>` compiler macro for declaring a component's exposed
- * instance properties when it is accessed by a parent component via template
- * refs.
- *
- * `<script setup>` components are closed by default - i.e. variables inside
- * the `<script setup>` scope is not exposed to parent unless explicitly exposed
- * via `defineExpose`.
- *
- * This is only usable inside `<script setup>`, is compiled away in the
- * output and should **not** be actually called at runtime.
- */
-export function defineExpose<
-  Exposed extends Record<string, any> = Record<string, any>
->(exposed?: Exposed): void
-
-type NotUndefined<T> = T extends undefined ? never : T
-
-type InferDefaults<T> = {
-  [K in keyof T]?: InferDefault<T, NotUndefined<T[K]>>
-}
-
-type InferDefault<P, T> = T extends
-  | null
-  | number
-  | string
-  | boolean
-  | symbol
-  | Function
-  ? T | ((props: P) => T)
-  : (props: P) => T
-
-type PropsWithDefaults<Base, Defaults> = Base & {
-  [K in keyof Defaults]: K extends keyof Base
-    ? Defaults[K] extends undefined
-      ? Base[K]
-      : NotUndefined<Base[K]>
-    : never
-}
-
-/**
- * Vue `<script setup>` compiler macro for providing props default values when
- * using type-based `defineProps` declaration.
- *
- * Example usage:
- * ```ts
- * withDefaults(defineProps<{
- *   size?: number
- *   labels?: string[]
- * }>(), {
- *   size: 3,
- *   labels: () => ['default label']
- * })
- * ```
- *
- * This is only usable inside `<script setup>`, is compiled away in the output
- * and should **not** be actually called at runtime.
- */
-export function withDefaults<Props, Defaults extends InferDefaults<Props>>(
-  props: Props,
-  defaults: Defaults
-): PropsWithDefaults<Props, Defaults>
-
-// make them global
-type _defineProps = typeof defineProps
-type _defineEmits = typeof defineEmits
-type _defineExpose = typeof defineExpose
-type _withDefaults = typeof withDefaults
-
-declare global {
-  const defineProps: _defineProps
-  const defineEmits: _defineEmits
-  const defineExpose: _defineExpose
-  const withDefaults: _withDefaults
-}
diff --git a/types/vnode.d.ts b/types/vnode.d.ts
deleted file mode 100644
index 533e61f169b..00000000000
--- a/types/vnode.d.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import { StyleValue } from './jsx'
-import { Vue } from './vue'
-import { DirectiveFunction, DirectiveOptions } from './options'
-import { Ref } from './v3-generated'
-import { ComponentPublicInstance } from './v3-component-public-instance'
-
-/**
- * For extending allowed non-declared props on components in TSX
- */
-export interface ComponentCustomProps {}
-
-/**
- * Default allowed non-declared props on component in TSX
- */
-export interface AllowedComponentProps {
-  class?: unknown
-  style?: unknown
-}
-
-export type ScopedSlot = (props: any) => ScopedSlotReturnValue
-type ScopedSlotReturnValue =
-  | VNode
-  | string
-  | boolean
-  | number
-  | null
-  | undefined
-  | ScopedSlotReturnArray
-interface ScopedSlotReturnArray extends Array<ScopedSlotReturnValue> {}
-
-// Scoped slots are guaranteed to return Array of VNodes starting in 2.6
-export type NormalizedScopedSlot = (props: any) => ScopedSlotChildren
-export type ScopedSlotChildren = VNode[] | undefined
-
-// Relaxed type compatible with $createElement
-export type VNodeChildren =
-  | VNodeChildrenArrayContents
-  | [ScopedSlot]
-  | string
-  | boolean
-  | number
-  | null
-  | undefined
-export interface VNodeChildrenArrayContents
-  extends Array<VNodeChildren | VNode> {}
-
-export interface VNode {
-  tag?: string
-  data?: VNodeData
-  children?: VNode[]
-  text?: string
-  elm?: Node
-  ns?: string
-  context?: Vue
-  key?: string | number | symbol | boolean
-  componentOptions?: VNodeComponentOptions
-  componentInstance?: Vue
-  parent?: VNode
-  raw?: boolean
-  isStatic?: boolean
-  isRootInsert: boolean
-  isComment: boolean
-}
-
-export interface VNodeComponentOptions {
-  Ctor: typeof Vue
-  propsData?: object
-  listeners?: object
-  children?: VNode[]
-  tag?: string
-}
-
-export type VNodeRef =
-  | string
-  | Ref
-  | ((
-      ref: Element | ComponentPublicInstance | null,
-      refs: Record<string, any>
-    ) => void)
-
-export interface VNodeData {
-  key?: string | number
-  slot?: string
-  scopedSlots?: { [key: string]: ScopedSlot | undefined }
-  ref?: VNodeRef
-  refInFor?: boolean
-  tag?: string
-  staticClass?: string
-  class?: any
-  staticStyle?: { [key: string]: any }
-  style?: StyleValue
-  props?: { [key: string]: any }
-  attrs?: { [key: string]: any }
-  domProps?: { [key: string]: any }
-  hook?: { [key: string]: Function }
-  on?: { [key: string]: Function | Function[] }
-  nativeOn?: { [key: string]: Function | Function[] }
-  transition?: object
-  show?: boolean
-  inlineTemplate?: {
-    render: Function
-    staticRenderFns: Function[]
-  }
-  directives?: VNodeDirective[]
-  keepAlive?: boolean
-}
-
-export interface VNodeDirective {
-  name: string
-  value?: any
-  oldValue?: any
-  expression?: string
-  arg?: string
-  oldArg?: string
-  modifiers?: { [key: string]: boolean }
-  def?: DirectiveFunction | DirectiveOptions
-}
diff --git a/types/vue.d.ts b/types/vue.d.ts
index f158cf01716..764f2654255 100644
--- a/types/vue.d.ts
+++ b/types/vue.d.ts
@@ -1,446 +1,83 @@
 import {
   Component,
   AsyncComponent,
-  ComponentOptions,
-  FunctionalComponentOptions,
+  FilterOptions,
   DirectiveOptions,
   DirectiveFunction,
-  RecordPropsDefinition,
-  ThisTypedComponentOptionsWithArrayProps,
-  ThisTypedComponentOptionsWithRecordProps,
-  WatchOptions
-} from './options'
-import { VNode, VNodeData, VNodeChildren, NormalizedScopedSlot } from './vnode'
-import { PluginFunction, PluginObject } from './plugin'
-import { DefineComponent } from './v3-define-component'
-import { nextTick, UnwrapNestedRefs, ShallowUnwrapRef } from './v3-generated'
-import {
-  UnwrapMixinsType,
-  IntersectionMixin
-} from './v3-component-public-instance'
-import {
-  ExtractComputedReturns,
-  ComponentOptionsMixin
-} from './v3-component-options'
-import { Directive, ObjectDirective } from './v3-directive'
-
-export interface CreateElement {
-  (
-    tag?:
-      | string
-      | Component<any, any, any, any>
-      | AsyncComponent<any, any, any, any>
-      | (() => Component),
-    children?: VNodeChildren
-  ): VNode
-  (
-    tag?:
-      | string
-      | Component<any, any, any, any>
-      | AsyncComponent<any, any, any, any>
-      | (() => Component),
-    data?: VNodeData,
-    children?: VNodeChildren
-  ): VNode
-}
-
-type NeverFallback<T, D> = [T] extends [never] ? D : T
-
-export interface Vue<
-  Data = Record<string, any>,
-  Props = Record<string, any>,
-  Instance = never,
-  Options = never,
-  Emit = (event: string, ...args: any[]) => Vue
-> {
-  // properties with different types in defineComponent()
-  readonly $data: Data
-  readonly $props: Props
-  readonly $parent: NeverFallback<Instance, Vue> | null
-  readonly $root: NeverFallback<Instance, Vue>
-  readonly $children: NeverFallback<Instance, Vue>[]
-  readonly $options: NeverFallback<Options, ComponentOptions<Vue>>
-  $emit: Emit
-
-  // Vue 2 only or shared
-  readonly $el: Element
-  readonly $refs: {
-    [key: string]:
-      | NeverFallback<Instance, Vue>
-      | Vue
-      | Element
-      | (NeverFallback<Instance, Vue> | Vue | Element)[]
-      | undefined
-  }
-  readonly $slots: { [key: string]: VNode[] | undefined }
-  readonly $scopedSlots: { [key: string]: NormalizedScopedSlot | undefined }
-  readonly $isServer: boolean
-
-  readonly $ssrContext: any
-  readonly $vnode: VNode
-  readonly $attrs: Record<string, string>
-  readonly $listeners: Record<string, Function | Function[]>
+  ComponentOptions,
+  WatchHandler,
+  WatchOptions,
+  TransitionOptions
+} from './options';
+import { PluginObject, PluginFunction } from './plugin'
+
+type Dictionary<T> = {
+  [key: string]: T;
+};
+
+type Plugin<T> = PluginFunction<T> | PluginObject<T>;
+type Filter = Function | FilterOptions;
+type Directive = DirectiveFunction<Vue> | DirectiveOptions<Vue>;
+
+export declare class Vue {
+  constructor(options?: ComponentOptions<Vue>);
+
+  $data: any;
+  readonly $el: HTMLElement;
+  readonly $options: ComponentOptions<Vue>;
+  readonly $parent: Vue;
+  readonly $root: Vue;
+  readonly $children: Vue[];
+  readonly $refs: Dictionary<Vue | Vue[]>;
+  readonly $els: Dictionary<Element | Element[]>;
 
-  $mount(elementOrSelector?: Element | string, hydrating?: boolean): this
-  $forceUpdate(): void
-  $destroy(): void
-  $set: typeof Vue.set
-  $delete: typeof Vue.delete
   $watch(
-    expOrFn: string,
-    callback: (this: this, n: any, o: any) => void,
+    expOrFn: string | Function,
+    callback: WatchHandler<this>,
     options?: WatchOptions
-  ): () => void
-  $watch<T>(
-    expOrFn: (this: this) => T,
-    callback: (this: this, n: T, o: T) => void,
-    options?: WatchOptions
-  ): () => void
-  $on(event: string | string[], callback: Function): this
-  $once(event: string | string[], callback: Function): this
-  $off(event?: string | string[], callback?: Function): this
-  $nextTick: typeof nextTick
-  $createElement: CreateElement
-}
-
-export type CombinedVueInstance<
-  Instance extends Vue,
-  Data,
-  Methods,
-  Computed,
-  Props,
-  SetupBindings = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
-  PublicMixin = IntersectionMixin<Mixin> & IntersectionMixin<Extends>
-> = UnwrapNestedRefs<UnwrapMixinsType<PublicMixin, 'D'>> &
-  Data &
-  UnwrapMixinsType<PublicMixin, 'M'> &
-  Methods &
-  ExtractComputedReturns<UnwrapMixinsType<PublicMixin, 'C'>> &
-  Computed &
-  UnwrapMixinsType<PublicMixin, 'P'> &
-  Props &
-  Instance &
-  ShallowUnwrapRef<UnwrapMixinsType<PublicMixin, 'B'>> &
-  (SetupBindings extends void ? {} : SetupBindings)
-
-export type ExtendedVue<
-  Instance extends Vue,
-  Data,
-  Methods,
-  Computed,
-  Props,
-  SetupBindings = {},
-  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-  Extends extends ComponentOptionsMixin = ComponentOptionsMixin
-> = VueConstructor<
-  CombinedVueInstance<
-    Instance,
-    Data,
-    Methods,
-    Computed,
-    Props,
-    SetupBindings,
-    Mixin,
-    Extends
-  > &
-    Vue
->
-
-export interface VueConfiguration {
-  silent: boolean
-  optionMergeStrategies: any
-  devtools: boolean
-  productionTip: boolean
-  performance: boolean
-  errorHandler(err: Error, vm: Vue, info: string): void
-  warnHandler(msg: string, vm: Vue, trace: string): void
-  ignoredElements: (string | RegExp)[]
-  keyCodes: { [key: string]: number | number[] }
-  async: boolean
-}
-
-export interface VueConstructor<V extends Vue = Vue> {
-  /**
-   * new with array props
-   */
-  new <
-    Data = object,
-    Methods = object,
-    Computed = object,
-    PropNames extends string = never,
-    SetupBindings = {},
-    Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-    Extends extends ComponentOptionsMixin = ComponentOptionsMixin
-  >(
-    options?: ThisTypedComponentOptionsWithArrayProps<
-      V,
-      Data,
-      Methods,
-      Computed,
-      PropNames,
-      SetupBindings,
-      Mixin,
-      Extends
-    >
-  ): CombinedVueInstance<
-    V,
-    Data,
-    Methods,
-    Computed,
-    Record<PropNames, any>,
-    SetupBindings,
-    Mixin,
-    Extends
-  >
-
-  /**
-   * new with object props
-   * ideally, the return type should just contain Props,
-   * not Record<keyof Props, any>. But TS requires to have Base constructors
-   * with the same return type.
-   */
-  new <
-    Data = object,
-    Methods = object,
-    Computed = object,
-    Props = object,
-    SetupBindings = {},
-    Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-    Extends extends ComponentOptionsMixin = ComponentOptionsMixin
-  >(
-    options?: ThisTypedComponentOptionsWithRecordProps<
-      V,
-      Data,
-      Methods,
-      Computed,
-      Props,
-      SetupBindings,
-      Mixin,
-      Extends
-    >
-  ): CombinedVueInstance<
-    V,
-    Data,
-    Methods,
-    Computed,
-    Record<keyof Props, any>,
-    SetupBindings,
-    Mixin,
-    Extends
-  >
-
-  /**
-   * new with no props
-   */
-  new (options?: ComponentOptions<V>): CombinedVueInstance<
-    V,
-    object,
-    object,
-    object,
-    Record<keyof object, any>,
-    {}
-  >
-
-  /**
-   * extend with array props
-   */
-  extend<
-    Data,
-    Methods,
-    Computed,
-    PropNames extends string = never,
-    SetupBindings = {},
-    Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-    Extends extends ComponentOptionsMixin = ComponentOptionsMixin
-  >(
-    options?: ThisTypedComponentOptionsWithArrayProps<
-      V,
-      Data,
-      Methods,
-      Computed,
-      PropNames,
-      SetupBindings,
-      Mixin,
-      Extends
-    >
-  ): ExtendedVue<
-    V,
-    Data,
-    Methods,
-    Computed,
-    Record<PropNames, any>,
-    SetupBindings,
-    Mixin,
-    Extends
-  >
-
-  /**
-   * extend with object props
-   */
-  extend<
-    Data,
-    Methods,
-    Computed,
-    Props,
-    SetupBindings = {},
-    Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-    Extends extends ComponentOptionsMixin = ComponentOptionsMixin
-  >(
-    options?: ThisTypedComponentOptionsWithRecordProps<
-      V,
-      Data,
-      Methods,
-      Computed,
-      Props,
-      SetupBindings,
-      Mixin,
-      Extends
-    >
-  ): ExtendedVue<
-    V,
-    Data,
-    Methods,
-    Computed,
-    Props,
-    SetupBindings,
-    Mixin,
-    Extends
-  >
-
-  /**
-   * extend with functional + array props
-   */
-  extend<PropNames extends string = never>(
-    definition: FunctionalComponentOptions<Record<PropNames, any>, PropNames[]>
-  ): ExtendedVue<V, {}, {}, {}, Record<PropNames, any>, {}>
-
-  /**
-   * extend with functional + object props
-   */
-  extend<Props>(
-    definition: FunctionalComponentOptions<Props, RecordPropsDefinition<Props>>
-  ): ExtendedVue<V, {}, {}, {}, Props, {}>
-
-  /**
-   * extend with no props
-   */
-  extend(options?: ComponentOptions<V>): ExtendedVue<V, {}, {}, {}, {}, {}>
-
-  nextTick<T>(callback: (this: T) => void, context?: T): void
-  nextTick(): Promise<void>
-  set<T>(object: object, key: string | number, value: T): T
-  set<T>(array: T[], key: number, value: T): T
-  delete(object: object, key: string | number): void
-  delete<T>(array: T[], key: number): void
-
-  directive(
-    id: string,
-    definition?: DirectiveOptions | DirectiveFunction
-  ): DirectiveOptions
-  directive(
-    id: string,
-    definition?: Directive
-  ): ObjectDirective
-  filter(id: string, definition?: Function): Function
-
-  component(id: string): VueConstructor
-  component<VC extends VueConstructor>(id: string, constructor: VC): VC
-  component<Data, Methods, Computed, Props, SetupBindings>(
-    id: string,
-    definition: AsyncComponent<Data, Methods, Computed, Props>
-  ): ExtendedVue<V, Data, Methods, Computed, Props, SetupBindings>
-  component<
-    Data,
-    Methods,
-    Computed,
-    PropNames extends string = never,
-    SetupBindings = {},
-    Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-    Extends extends ComponentOptionsMixin = ComponentOptionsMixin
-  >(
-    id: string,
-    definition?: ThisTypedComponentOptionsWithArrayProps<
-      V,
-      Data,
-      Methods,
-      Computed,
-      PropNames,
-      SetupBindings,
-      Mixin,
-      Extends
-    >
-  ): ExtendedVue<
-    V,
-    Data,
-    Methods,
-    Computed,
-    Record<PropNames, any>,
-    SetupBindings,
-    Mixin,
-    Extends
-  >
-  component<
-    Data,
-    Methods,
-    Computed,
-    Props,
-    SetupBindings,
-    Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
-    Extends extends ComponentOptionsMixin = ComponentOptionsMixin
-  >(
-    id: string,
-    definition?: ThisTypedComponentOptionsWithRecordProps<
-      V,
-      Data,
-      Methods,
-      Computed,
-      Props,
-      SetupBindings,
-      Mixin,
-      Extends
-    >
-  ): ExtendedVue<V, Data, Methods, Computed, Props, SetupBindings>
-  component<PropNames extends string>(
-    id: string,
-    definition: FunctionalComponentOptions<Record<PropNames, any>, PropNames[]>
-  ): ExtendedVue<V, {}, {}, {}, Record<PropNames, any>, {}>
-  component<Props>(
-    id: string,
-    definition: FunctionalComponentOptions<Props, RecordPropsDefinition<Props>>
-  ): ExtendedVue<V, {}, {}, {}, Props, {}>
-  component(
-    id: string,
-    definition?: ComponentOptions<V>
-  ): ExtendedVue<V, {}, {}, {}, {}, {}>
-  component<T extends DefineComponent<any, any, any, any, any, any, any, any>>(
-    id: string,
-    definition?: T
-  ): T
-
-  use<T>(
-    plugin: PluginObject<T> | PluginFunction<T>,
-    options?: T
-  ): VueConstructor<V>
-  use(
-    plugin: PluginObject<any> | PluginFunction<any>,
-    ...options: any[]
-  ): VueConstructor<V>
-  mixin(mixin: VueConstructor | ComponentOptions<Vue>): VueConstructor<V>
-  compile(template: string): {
-    render(createElement: typeof Vue.prototype.$createElement): VNode
-    staticRenderFns: (() => VNode)[]
-  }
-
-  observable<T>(obj: T): T
-
-  util: {
-    warn(msg: string, vm?: InstanceType<VueConstructor>): void
+  ): () => void;
+  $get(expression: string): any;
+  $set<T>(keypath: string, value: T): T;
+  $delete(key: string): void;
+  $eval(expression: string): any;
+  $interpolate(templateString: string): any;
+  $log(keypath?: string): void;
+
+  $on(event: string, callback: Function): this;
+  $once(event: string, callback: Function): this;
+  $off(event: string, callback?: Function): this;
+  $emit(event: string, ...args: any[]): this;
+  $broadcast(event: string, ...args: any[]): this;
+  $dispatch(event: string, ...args: any[]): this;
+
+  $appendTo(elementOrSelector: HTMLElement | string, callback?: Function): this;
+  $before(elementOrSelector: HTMLElement | string, callback?: Function): this;
+  $after(elementOrSelector: HTMLElement | string, callback?: Function): this;
+  $remove(callback?: Function): this;
+  $nextTick(callback: (this: this) => void): void;
+
+  $mount(elementOrSelector?: HTMLElement | string): this;
+  $destroy(remove?: boolean): void;
+
+  static extend(options: ComponentOptions<Vue>): typeof Vue;
+  static nextTick(callback: () => void, context?: any[]): void;
+  static set<T>(object: any, key: string, value: T): T;
+  static delete(object: any, key: string): void;
+  static directive<T extends Directive>(id: string, definition?: T): T;
+  static elementDirective<T extends Directive>(id: string, definition?: T): T;
+  static filter<T extends Filter>(id: string, definition?: T): T;
+  static component<V extends Vue>(id: string, definition?: ComponentOptions<V> | AsyncComponent): ComponentOptions<V>;
+  static transition(id: string, hooks?: TransitionOptions): TransitionOptions;
+  static partial(id: string, partial?: string): string;
+  static use<T>(plugin: Plugin<T>, options?: T): void;
+  static mixin(mixin: Component): void;
+
+  static config: {
+    debug: boolean;
+    delimiters: [string, string];
+    unsafeDelimiters: [string, string];
+    silent: boolean;
+    async: boolean;
+    devtools: boolean;
   }
-
-  config: VueConfiguration
-  version: string
 }
-
-export const Vue: VueConstructor
diff --git a/vitest.config.ts b/vitest.config.ts
deleted file mode 100644
index 52e5ea66489..00000000000
--- a/vitest.config.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { resolve as _resolve } from 'path'
-import { defineConfig } from 'vitest/config'
-
-const resolve = (p: string) => _resolve(__dirname, p)
-
-export default defineConfig({
-  resolve: {
-    alias: {
-      compiler: resolve('src/compiler'),
-      core: resolve('src/core'),
-      server: resolve('packages/server-renderer/src'),
-      sfc: resolve('packages/compiler-sfc/src'),
-      shared: resolve('src/shared'),
-      web: resolve('src/platforms/web'),
-      v3: resolve('src/v3'),
-      vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
-      types: resolve('src/types')
-    }
-  },
-  define: {
-    __DEV__: true,
-    __TEST__: true
-  },
-  test: {
-    globals: true,
-    environment: 'jsdom',
-    setupFiles: resolve('test/vitest.setup.ts')
-  }
-})